React Native
Section 1: Introduction and Setup
=> Introduction
- Welcome to an exciting series on React Native for beginners.
What is React Native?
React Native is an open-source framework for building native Android and iOS applications using React.
Leveraging JavaScript, you can access platform-specific APIs while employing React components to define the appearance and behavior of
your user interface.
Why Learn React Native?
Traditionally, developing an iOS app requires knowledge of Swift or Objective-C, while building an Android app requires Java or Kotlin
expertise.
With React Native, you can create an app that works seamlessly on both platforms, saving time and effort.
Learning React Native involves a minimal learning curve if you already have experience with React.
React Native is in high demand in the job market, allowing businesses to have a single team proficient in React Native instead of
separate iOS and Android developers.
The expertise in React can also extend to web app development.
React Native is used by companies like Microsoft, Meta, Tesla, Pinterest, and Discord, making it a valuable addition to your skill set.
Prerequisites for Learning React Native
A solid understanding of JavaScript and the fundamentals of React is essential.
Concepts like function components, props, state, JSX, and hooks are prerequisites.
If you're new to React, don't worry. There is an extensive tutorial series covering React from beginner to advanced levels.
Getting Started
All the source code related to this series can be found in my GitHub repository (linked in the description).
Subscribe to the channel and stay tuned for the next video as we start our journey of learning the various concepts of React Native.
=> Expo vs React Native
- let's explore a vital topic: Expo versus React Native.
React Native
- React Native is an open-source framework maintained by Meta or Facebook, designed for building cross-platform apps.
Expo
Expo, on the other hand, is an independent open-source framework maintained by Expo itself.
While both frameworks have the common goal of developing Android and iOS apps, they differ significantly in terms of maintenance, ease of setup, and platform compatibility.
Advantages of Expo
Expo provides a streamlined experience and a suite of tools and services built around React Native, greatly simplifying the development process.
Expo is to React Native what Next.js is to React.
It offers a simplified setup compared to plain React Native, which can be challenging and time-consuming.
One of the key advantages of Expo is the ability to develop your app on Windows or Linux and still run it on a physical iPhone.
Expo has evolved significantly over the years and now supports nearly all the features necessary for building enterprise mobile apps.
Flexibility of Expo
In the event that you require access to native Android or iOS code, Expo allows you to eject your app and work with a plain React Native code base.
This flexibility ensures that you have the option to leverage the additional functionalities provided by Expo while still tapping into native code when necessary.
Expo as the Default Choice
If you're new to React Native and exploring the official React Native documentation, you'll notice that Expo is the default choice for setting up your development environment.
This endorsement by the React Native community highlights Expo as the optimal starting point for beginners.
Expo provides a smoother onboarding experience and a robust set of tools to kick-start your React Native journey, which is what we will be using in this series.
Conclusion
- Join me in the next video where we will create our first React Native project using Expo. Thank you.
=> Hello World
Setting Up Development Environment
Install Node.js from node.js.org, the latest stable release.
Update Node.js if already installed.
Use VS Code as the code editor, which can be downloaded from code.visualstudio.com.
Create a folder called "React Native Course" and open it in VS Code.
This folder will serve as our workspace throughout the series.
Creating a React Native Project with Expo
- Open the terminal in VS Code (shortcut: Control + `) and enter the following command:
npx create-expo-app@latest [project-name]
This command will create a new React Native project with the Expo package already installed.
The project directory will be created, and all necessary dependencies will be installed.
Understanding Project Files and Folders
Take a few minutes to understand the different files and folders generated by create-expo-app.
The
package.jsonfile contains project dependencies, scripts, and metadata.The
package-lock.jsonfile ensures consistent installation of project dependencies.The
babel.config.jsfile serves as the Babel configuration file.The
app.jsonfile contains configuration options for the project.The
app.jsfile serves as the default screen of the project.The
.gitignorefile specifies files and folders that shouldn't be tracked by the Version Control System.The
node_modulesfolder houses all project dependencies.The
assetsfolder contains various resources, such as icons and splash screens.
Running the React Native Expo App
It's finally time to run our first React Native Expo app.
Follow the instructions in the terminal to run the project.
=> Running the App on a Device
Now that we have learned how to generate a new Expo project and gained an understanding of the project's different files and folders, it's time to run our application.
There are several methods available for running the app on different devices:
iOS device
Android device
iOS simulator
Android emulator
We will focus on running the app on a real device.I will be demonstrating the process on an iPhone, but I will also provide instructions for running the app on an Android device.
Steps to Run the App on a Real Device
Navigate to the project folder and run the command
npm start.- This will generate a QR code which we will use shortly.
Download the Expo Go app on your device.
- You can find it on the App Store for iPhones or the Play Store for Android devices.
Ensure that both your computer and the mobile device are connected to the same Wi-Fi network.On your iPhone, open the Camera app and scan the QR code displayed in the terminal.
If you have an Android device, use the QR code scanner within the Expo Go app.
After a few seconds, you should see the Hello World app running on your device.
- The device screen may be mirrored on your computer screen.
To test the app, go back toapp.jsand change the text to "Hello World".- Save the file, and you will notice the change reflected on your device almost instantly.
=> Running the App on an iOS Simulator
let's explore how to run the app on an iOS simulator.
Please note that running iOS simulators is only possible on a Mac and not on Windows or Linux systems.
Steps to Run the App on an iOS Simulator
Navigate to the project folder in the terminal and execute the command
npm start.Download Xcode from the App Store and grant the necessary permissions it requests.
- Note: Xcode may already be downloaded if you have a Mac.
In the terminal, find out the shortcut for opening the iOS simulator. It is
I, so pressIon your keyboard.- Within a few seconds, the iPhone simulator should open and prompt you to open the app in Expo Go.
Grant the necessary permissions and you will see the Expo app running on the simulator.
Try changing the text to "Hello iOS Simulator" in
app.js.- Save the file and you will see the change reflecting in the simulator.
By default, the iPhone SE (third generation) simulator is open. If you prefer to use a different device, go to File > Open Simulator and select a device (e.g., iPhone 14 Pro S).
Go back to VS Code, with focus in the terminal, and press
Ionce again.- This will open the application in Expo Go on the selected device, and you will have your app up and running.
=> Running the App on an Android Emulator
- we will explore how to run our application on an Android emulator.
Steps to Run the App on an Android Emulator
Visit
developer.android.com/studioand download Android Studio.- It is available for Windows, Mac OS, and Linux.
Install Android Studio.
Launch the setup Wizard and click "Next" on the welcome screen.
Choose the standard setup type and click "Next".
Proceed through the setup by selecting the desired color theme and accepting the terms.
Finally, click "Finish" on the final review screen.
The installation process may take a few minutes.
Once Android Studio is installed, you should see a different welcome screen.
Click on "More Actions" and select "Virtual Device Manager".
By default, there will be a Pixel 3 device available. However, you can create a new device if you wish to test on different devices.
Click on the "Create Device" button to create a new virtual device.
Choose a device that you wish to test on. For example, select the Pixel 4 device where the Play Store icon is displayed.
Select the Android API level. For example, select the latest API level (API 34).
Provide a name for your virtual device (e.g., "Code Evolution Test") and click "Finish" on the verify configuration screen.
This will create a Pixel 4 virtual device in Android Studio.
Run the device by clicking on the play button.
With the Android emulator running in the background, return to VS Code.
Press "A" to run our application on the opened device.
This will install Expo Go on the virtual device and launch your application.
Within a few seconds, you will see the Expo app running on the virtual device.
Try changing the text in
app.jsto "Hello from Android".- You will notice the text reflecting right away.
Conclusion and Next Steps
These are the different methods for running an Expo app on your physical device, an iOS simulator, or an Android emulator.
Throughout the series, we will primarily focus on running our app on the iOS simulator and Android emulator.
However, if you prefer, you can also run it on your own physical device.
In the next section, let's take a look at the core components React Native offers to build native mobile applications.
If you're enjoying the content, please leave a like and subscribe to the channel. I'll see you in the next video.
Section 2: Components
=> Core Components
the core components in React Native
In this section, we will explore the core components in React Native.
When we use React to create web user interfaces, we often utilize HTML tags such as
<div>,<span>, and<p>.Here's an example React component that renders "Hello, World!" in the browser using
<div>and<p>tags.
import React from 'react'
const HelloWorld = () => {
return (
<div>
<p>Hello, World!</p>
</div>
)
}
export default HelloWorld
It's crucial to note that JSX elements we write for the web are specific to the browser's understanding of HTML.
However, when working with React Native, these elements won't work because React Native does not incorporate the concept of a Document Object Model (DOM).
In Android and iOS development, a fundamental building block called a "View" is employed for user interfaces.
A view is a small rectangular element on the screen that can display text, images, or respond to user input.
In Android development, views are written in Kotlin or Java, while in iOS development, Swift or Objective-C is used.
With React Native, this process becomes simpler as you can create these views using JavaScript through React components. At runtime, React Native generates the corresponding Android and iOS views for these components.
React Native offers a collection of essential rebuilt components known as "Core Components" that are readily available for building your native app's user interface.
Core Components Table
| React Native Core Component | Android Equivalent | iOS Equivalent | Web Equivalent |
|---|
| View | View | UIView | <div> |
| Text | TextView | UILabel | <p> |
| Image | ImageView | UIImageView | <img> |
| TextInput | EditText | UITextField | <input> |
| ScrollView | ScrollView | UIScrollView | <div> |
| Button | Button | UIButton | <button> |
Pause the video and take a good look at each row and column in this table.
A notable difference between creating React components for the web and React Native is that in React Native, you need to import core components from the React Native Library, ...
import React from 'react'
import { View, Text } from 'react-native' /// import core components from the React Native Library
const MyComponent = () => {
return (
<View>
<Text>Hello, World!</Text>
</View>
)
}
export default MyComponent
whereas in React for web development, you don't import HTML elements like
<div>,<span>, or<p>.Before we proceed, it's important to quickly highlight a point about styling. We will cover styling in detail in the next section, but for this section, we will apply some basic inline styles that might be familiar if you have written HTML and CSS for the web.
Conclusion
- With an understanding of what core components are in React Native, let's take a closer look at the most important ones in the next video.
=> View Component
Intro
The View component is a fundamental code component in React Native.It serves as a building block for creating user interfaces.
The View component functions as a container that supports layouts using flexbox styling, touch handling, and accessibility controls.
In web development terms, the View component can be compared to the div tag.
When working with React Native, the View component is typically nested inside other views and can have zero or more children of any type.
It provides the structure and organization for other components in the UI.
Setting up the Project
- Create a new Expo project using the command
npx create-expo-app core-components
Make sure you are running the latest version of Expo (version 49) by checking the package.json file.
Start the development server by running the command
npm start
ios and android simulators
Open the iOS simulator by pressing the key "I" in the terminal.
Open the Android emulator in Android Studio.
Run the app on the Android emulator by pressing the key "A" in the terminal.
› Using Expo Go
› Press s │ switch to development build
› Press a │ open Android
› Press i │ open iOS simulator
› Press w │ open web
› Press j │ open debugger
› Press r │ reload app
› Press m │ toggle menu
› Press o │ open project code in your editor
› Press ? │ show all commands
Logs for your project will appear below. Press Ctrl+C to exit.
- Verify that the app is running on both the iOS simulator and Android emulator.
Starting with a Clean Slate
Delete the existing code in the "app.js" file.
Import the View component from the React Native library.
Define a new component called "App" and make it the default export.
Within the JSX code, return the View component.
import React from 'react'
import { View } from 'react-native'
function App() {
return <View>{/* Your code here */}</View>
}
export default App
Displaying the View Component
Add a background color to the View component using inline styles.
Set the style prop as an object and set the background color to "plum".
Save the file and observe the iOS simulator and Android emulator.
import React from 'react'
import { View } from 'react-native'
function App() {
return <View style={{ backgroundColor: 'plum' }}>{/* Your code here */}</View>
}
export default App
Expanding the View Component
Add the style property "flex: 1" to the View component.
This will make the View component flexibly expand and take up all available space on the screen.
Save the file and observe the expanded View component.
import React from 'react'
import { View } from 'react-native'
function App() {
return <View style={{ flex: 1, backgroundColor: 'plum' }}>{/* Your code here */}</View>
}
export default App
Nesting Views within the View Component
Create a new View component with a light blue background color.
Specify its height and width as 200.
Duplicate the View component and change its background color to light green.
Save the file and observe the nested views within the outer View component.
import React from 'react'
import { View } from 'react-native'
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum' }}>
<View style={{ backgroundColor: 'lightblue', height: 200, width: 200 }} />
<View style={{ backgroundColor: 'lightgreen', height: 200, width: 200 }} />
</View>
)
}
export default App
Summary
The View component is a core component in React Native that serves as a container for other components.
It supports layout and styling using flexbox.
Remember to import the View component before using it.
Nested views can be used to create complex user interfaces.
=> Text Component
Intro
The text component is a component for displaying text.
It supports nesting, styling, and touch handling.
Depending on the target platform,
React Native will translate this componentto either a UI text view for iOS, a text view for Android, or a paragraph tag for the web.
Implementing an Example
To start, let's write some code within the outer view component.
Delete the two nested views and instead add the text "Hello World".
import React from 'react'
import { View } from 'react-native'
function App() {
return <View style={{ flex: 1, backgroundColor: 'plum' }}>Hello World</View>
}
export default App
When we save the file,
we see an error.This error occurs because
in React Native, all text nodes must be wrapped inside a text component.Directly placing text under a view is not allowed.
This is a common error you will run into when starting with React Native.
To fix this error, we need to import the text component from React Native and wrap the "Hello World" text with it.
import React from 'react'
import { View, Text } from 'react-native'
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum' }}>
<Text>Hello World</Text>
</View>
)
}
export default App
- Once we make this change and save the file, we can now see the text being displayed in the simulator and the emulator.
Improving the Position
Now the position of the text is a little awkward, so let's add some padding for now on the outer view container.
Add a padding of 60.
import React from 'react'
import { View, Text } from 'react-native'
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Text>Hello World</Text>
</View>
)
}
export default App
Save the file.
This is much better. We're able to clearly see the "Hello World" text. Not perfect, but better.
Styling and Layout
As mentioned in the previous video, there is much more to learn about styling and layout with these components.
We will cover these topics in detail in the upcoming sections.
Nesting Text Components
Next, let's explore nesting text components.
For example, suppose we want to display the word "Hello" in white color.
To achieve this, we can wrap the "Hello" text with another pair of text component tags.
On this inner text component, we can add a style prop, which is an object, and set the color property to a value of "white".
import React from 'react'
import { View, Text } from 'react-native'
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Text>
<Text style={{ color: 'white' }}>Hello</Text> World
</Text>
</View>
)
}
export default App
- By doing this, the word "Hello" will appear in white color, while "World" remains in black.
Summary
The text component is essential for displaying text in React Native.
Remember that every text node within a view must be wrapped with the text component.
Additionally, nesting of text components is allowed.
=> Image Component
Intro
The image component enables us to display various types of images including static images, network images, and images from the local disk such as the camera roll.
React Native seamlessly translates the image component to platform-specific counterparts: UIImage view for iOS, ImageView for Android, and the image tag for the web.
Let's return to VS Code and understand more.
Rendering a Static Image
To begin, let's render a static image located in the assets folder.
Start by importing the image component from react native.
Next, import the image from the assets folder.
The const, let's call it logo image, and we use the required function from './assets/adaptive-icon.png'.
In the JSX code, invoke the image component and specify the source prop so image which is self-closing and we specify the source prop equal to logo image.
import React from 'react'
import { View, Text, Image } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Text>
<Text style={{ color: 'white' }}>Hello</Text> World
</Text>
<Image source={logoImage} />
</View>
)
}
export default App
If we save the file and check both the devices, we'll see that the image renders but appears too large.
To fix this, let's add a width and height to the image, so style prop with 300 and height 300.
import React from 'react'
import { View, Text, Image } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Text>
<Text style={{ color: 'white' }}>Hello</Text> World
</Text>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
</View>
)
}
export default App
- Save the file and now we can see the image within the view much better.
Loading a Network Image
Next, let's load an image by making a network request.
Duplicate the line with the image component and change the source prop to a remote image URI.
A convenient place to find placeholder images is picsum.photos.
Scroll down and find the URL for a square image. (
https://picsum/photos/200)Copy and paste the URL as a string into the source attribute.
Let's adjust the size to 300 by 300, (
https://picsum/photos/300)
import React from 'react'
import { View, Text, Image } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Text>
<Text style={{ color: 'white' }}>Hello</Text> World
</Text>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
<Image
source={'https://picsum.photos/200'}
style={{ width: 300, height: 300 }}
/>
</View>
)
}
export default App
When we save the file, we encounter a warning.
The warning is because the source prop expects a number but we have provided a string.
When using logo image as a value we are actually passing in a number that references the image in the assets folder.
In order to specify a remote image as the source, we need to convert the value into an object and include a key called uri.
The string is set as the value for uri.
import React from 'react'
import { View, Text, Image } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Text>
<Text style={{ color: 'white' }}>Hello</Text> World
</Text>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
<Image
source={{ uri: 'https://picsum.photos/200' }}
style={{ width: 300, height: 300 }}
/>
</View>
)
}
export default App
If we go back to our screens, we'll see the image being rendered.
It's very important to note that
specifying width and height is mandatory for Network images.For static images, React Native can infer the dimensions from the file data.
Setting a Background Image
Finally, let's explore setting a background image for a view.
React Native provides a second image component called
imageBackgroundfor this purpose.Import it at the top and then comment out the two image components in the JSX.
Instead, add the image background component opening and closing tags.
We pass the source attribute is equal to
logoImagewhich is the Adaptive icon.And then ,as children for the image background component, Let's add a text component that says IMAGE TEXT.
Save the file and we can observe that the image appears in the background with the text overlaid on top.
import React from 'react'
import { View, Text, Image, ImageBackground } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Text>
<Text style={{ color: 'white' }}>Hello</Text> World
</Text>
{/* <Image source={logoImage} style={{ width: 300, height: 300 }} />
<Image source={{ uri: 'https://picsum.photos/200' }} style={{ width: 300, height: 300 }} /> */}
<ImageBackground source={logoImage}>
<Text>IMAGE TEXT</Text>
</ImageBackground>
</View>
)
}
export default App
If you want the image to occupy the entire available space, rely on the flex property.
Style Flex set to 1.
import React from 'react'
import { View, Text, Image, ImageBackground } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Text>
<Text style={{ color: 'white' }}>Hello</Text> World
</Text>
{/* <Image source={logoImage} style={{ width: 300, height: 300 }} />
<Image source={{ uri: 'https://picsum.photos/200' }} style={{ width: 300, height: 300 }} /> */}
<ImageBackground
source={logoImage}
style={{ flex: 1 }}>
<Text>IMAGE TEXT</Text>
</ImageBackground>
</View>
)
}
export default App
We see the image now takes up the available space.
Let's not worry too much about centering the text for now, as I mentioned in every video we will cover styling and layouts in the upcoming sections.
Summary
In summary, the image component is utilized to render images in React Native.
You can display static images, network images, and even local images from the camera roll (although that topic is beyond the scope of this video).
To render an image, import the image component from React Native and specify the source prop.
For remote images, ensure to use the URI option.
To render a background image, utilize the image background component nesting the desired content inside.
=> ScrollView Component
Intro
View Component:
The View component is similar to a div tag in web development. However, it does not scroll automatically. It is used to define the structure and layout of the content.
Image Component:
The Image component is used to display images in React Native. It can be static or dynamic.
To demonstrate the difference between the web and React Native, we will use the View and Image components.
Implementing an Example
Keep the outer View component and one Image component from the previous video.
Keep the static image as it is.
import React from 'react'
import { View, Text, Image, ImageBackground } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
</View>
)
}
export default App
Adding Text Component
Add a lengthy Text component right after the Image component to emphasize the point.
The Text component contains some lorem ipsum text.
import React from 'react'
import { View, Text, Image, ImageBackground } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
<Text>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Qui eligendi perspiciatis ipsam
eaque modi, ducimus quae similique in at voluptatum cumque! Aliquam, quisquam id, placeat
neque a fugiat ea quam odio, repudiandae atque iste. Nulla at quaerat exercitationem
blanditiis aspernatur recusandae non laudantium omnis? Accusamus eaque mollitia optio? Illo,
aliquam! Repellat praesentium cum, earum iste iure ipsa accusamus, expedita, temporibus
optio corporis eius fugiat minus consequuntur aperiam est ullam nobis rerum. Autem quibusdam
unde molestiae? Similique repudiandae dolorum aut nobis hic aliquid doloribus obcaecati
beatae, fugit temporibus! Incidunt sint at ut id consectetur necessitatibus, natus possimus
sequi adipisci dignissimos minima accusantium ea ducimus culpa libero assumenda! Hic
doloremque alias placeat inventore quibusdam vitae neque facere, tempora excepturi saepe
ipsa numquam, ratione aspernatur tempore nisi incidunt distinctio? Doloremque possimus,
accusamus recusandae iste, ut doloribus sed dolores itaque, quaerat nostrum minima voluptas
perferendis corporis suscipit iusto mollitia deserunt. Commodi minima totam iure?
</Text>
</View>
)
}
export default App
Duplicating Image Component
- Duplicate the Image component by copying and pasting it.
import React from 'react'
import { View, Text, Image, ImageBackground } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
<Text>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Mollitia id ipsa veritatis, nemo
ad reiciendis blanditiis consequuntur fugit officia saepe tenetur doloribus illo sed ipsam
natus, qui temporibus unde ratione, sapiente in tempore debitis delectus nisi harum!
Voluptate voluptatem nulla esse qui similique aspernatur quam culpa cumque quia
voluptatibus. Quis, quod ex optio tenetur deserunt saepe officiis excepturi. Maiores
quisquam, corrupti hic consectetur animi voluptatum libero itaque nisi eaque esse laudantium
et nemo fuga laborum eligendi quasi accusamus dolor iure eos quibusdam recusandae. At
explicabo perferendis laudantium, tempore dolorum quos beatae voluptatum a tempora, minus
eius eligendi impedit blanditiis nulla!
</Text>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
</View>
)
}
export default App
Viewing the Content
After saving the file and returning to the simulator or emulator, we will notice that the first image is followed by the long text and then the image again, both in iOS and Android.
However, there is an issue - we cannot scroll down to see the full image. Clicking and dragging does not scroll.
Introducing ScrollView
React Native provides the ScrollView component specifically for scrolling purposes.
Import the ScrollView component from react native.
Replace the View component with ScrollView.
import React from 'react'
import { View, Text, Image, ScrollView } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
function App() {
return (
<ScrollView style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
<Text>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Mollitia id ipsa veritatis, nemo
ad reiciendis blanditiis consequuntur fugit officia saepe tenetur doloribus illo sed ipsam
natus, qui temporibus unde ratione, sapiente in tempore debitis delectus nisi harum!
Voluptate voluptatem nulla esse qui similique aspernatur quam culpa cumque quia
voluptatibus. Quis, quod ex optio tenetur deserunt saepe officiis excepturi. Maiores
quisquam, corrupti hic consectetur animi voluptatum libero itaque nisi eaque esse laudantium
et nemo fuga laborum eligendi quasi accusamus dolor iure eos quibusdam recusandae. At
explicabo perferendis laudantium, tempore dolorum quos beatae voluptatum a tempora, minus
eius eligendi impedit blanditiis nulla!
</Text>
<Image source={logoImage} style={{ width: 300, height: 300 }} />
</ScrollView>
)
Resolving Padding Issue
The ScrollView component has padding by default, which causes the issue of not being able to fully see the end of the second image.
To address this, nest the ScrollView within the View component.
This way, the ScrollView is bounded by the height of its parent, representing the entire available space with 60 pixels padding.
import React from 'react'
import { View, Text, Image, ScrollView } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<ScrollView>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
<Text>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Mollitia id ipsa veritatis, nemo
ad reiciendis blanditiis consequuntur fugit officia saepe tenetur doloribus illo sed ipsam
natus, qui temporibus unde ratione, sapiente in tempore debitis delectus nisi harum!
Voluptate voluptatem nulla esse qui similique aspernatur quam culpa cumque quia
voluptatibus. Quis, quod ex optio tenetur deserunt saepe officiis excepturi. Maiores
quisquam, corrupti hic consectetur animi voluptatum libero itaque nisi eaque esse
laudantium et nemo fuga laborum eligendi quasi accusamus dolor iure eos quibusdam
recusandae. At explicabo perferendis laudantium, tempore dolorum quos beatae voluptatum a
tempora, minus eius eligendi impedit blanditiis nulla!
</Text>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
</ScrollView>
</View>
)
}
export default App
After saving the file, the content becomes scrollable.
We can now scroll down to the end of the second image successfully.
Summary
unlike the div tag in web development, the View component in React Native doesn't scroll by default.
For scrollable content, you should utilize the ScrollView component and ensure it has a bounded height.
=> Button Component
Intro
The Button component allows users to trigger actions similar to the button component in the web
However, it's important to note that the button component has platform-specific rendering for iOS and Android
Implementing an Example
First, we need to import the Button component from React Native
Inside the View component, I'll invoke the Button component
import React from 'react'
import { View, Text, Image, ScrollView, Button } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button />
</View>
)
}
Unlike the HTML button element, the button component in React Native is a self-closing tag with no inner text or closing tag
To specify the button text, we use a prop called
titleLet's set it it to "Press"
import React from 'react'
import { View, Text, Image, ScrollView, Button } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button title='Press' />
</View>
)
}
- If we save the file and take a look at the devices, we can see the button component rendered with iOS styles on iPhone 14 Pro and Android styles on the Android virtual device
Handling the Press Event
In React Native, the button component provides an
onPressprop to handle the Press eventLet's add onPress equal to an arrow function that logs to the console "Button pressed"
import React from 'react'
import { View, Text, Image, ScrollView, Button } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => console.log('Button pressed')}
/>
</View>
)
}
When we press the button, the event handler is triggered and we see the log message in the terminal
The event handler can be more complex by defining a separate function and assigning it to the onPress event
Customization with Props
The third prop to highlight is the
colorprop, which allows easy customization of the button colorLet's set color to "midnight blue"
import React from 'react'
import { View, Text, Image, ScrollView, Button } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => console.log('Button pressed')}
color='midnightblue'
/>
</View>
)
}
Now, if we take a look at the UI, we can see the button with the new color applied
Lastly, there is the
disabledprop mainly used in form handling
import React from 'react'
import { View, Text, Image, ScrollView, Button } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => console.log('Button pressed')}
color='midnightblue'
disabled
/>
</View>
)
}
By adding the disabled prop and setting it to true by default, the button is styled as disabled and won't trigger any press events when pressed
It's usually better to manage a state variable to control the disabled prop value instead of directly assigning true or false
disabled = { state }
Summary
The
Buttoncomponent is used to trigger actions on pressYou can specify the
titleprop for the button textThe
onPressprop is used to handle the Press eventThe
colorprop is used to set the button colorThe
disabledprop is used to disable the button
=> Pressable Component
Intro
In the previous video, we learned about the Button component which allows us to trigger actions based on user interaction.
However, there are times when we need to trigger actions on the press of other elements such as images or text.
To address this requirement, React Native provides a component called
PressablePressable is a wrapper component that detects various stages of press interactions on its defined children
Implementing an example
Let's switch back to VS Code and explore an example to better understand its usage.
Behind the scenes, I have included an Image component and a Text component. Both of these are components we've already seen in previous videos.
import React from 'react'
import { View, Button, Image, Text } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => console.log('Button pressed')}
color='midnightblue'
/>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
<Text>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Qui eligendi perspiciatis ipsam
eaque modi, ducimus quae similique in at voluptatum cumque! Aliquam, quisquam id, placeat
neque a fugiat ea quam odio, repudiandae atque iste. Nulla at quaerat exercitationem
blanditiis aspernatur recusandae non laudantium omnis? Accusamus eaque mollitia optio? Illo,
aliquam! Repellat praesentium cum, earum iste iure ipsa accusamus, expedita, temporibus
optio corporis eius fugiat minus consequuntur aperiam est ullam nobis rerum. Autem quibusdam
unde molestiae? Similique repudiandae dolorum aut nobis hic aliquid doloribus obcaecati
beatae, fugit temporibus! Incidunt sint at ut id consectetur necessitatibus, natus possimus
sequi adipisci dignissimos minima accusantium ea ducimus culpa libero assumenda! Hic
doloremque alias placeat inventore quibusdam vitae neque facere
</Text>
</View>
)
}
- If we take a look at the two devices, we have the Button from the last video, the Image component, and the long Text component.
Getting Started with Pressable Component
To begin:
Import the Pressable component from React Native.
Wrap the desired elements with Pressable to detect press interactions. In our example, we'll wrap both the Image and Text components. So, the Image component is now a child of Pressable, and so is the Text component.
import React from 'react'
import { View, Button, Image, Text, Pressable } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => console.log('Button pressed')}
color='midnightblue'
/>
<Pressable>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>{' '}
</Pressable>
<Pressable>
<Text>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Qui eligendi perspiciatis ipsam
eaque modi, ducimus quae similique in at voluptatum cumque! Aliquam, quisquam id, placeat
neque a fugiat ea quam odio, repudiandae atque iste. Nulla at quaerat exercitationem
blanditiis aspernatur recusandae non laudantium omnis? Accusamus eaque mollitia optio?
Illo, aliquam! Repellat praesentium cum, earum iste iure ipsa accusamus, expedita,
temporibus optio corporis eius fugiat minus consequuntur aperiam est ullam nobis rerum.
Autem quibusdam unde molestiae? Similique repudiandae dolorum aut nobis hic aliquid
doloribus obcaecati beatae, fugit temporibus! Incidunt sint at ut id consectetur
necessitatibus, natus possimus sequi adipisci dignissimos minima accusantium ea ducimus
culpa libero assumenda! Hic doloremque alias placeat inventore quibusdam vitae neque
facere
</Text>
</Pressable>
</View>
)
}
Similar to the Button component, the Pressable component accepts the
onPressprop to handle the press event.Let's add it with an event handler:
import React from 'react'
import { View, Button, Image, Text, Pressable } from 'react-native'
const logoImage = require('./assets/adaptive-icon.png')
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => console.log('Button pressed')}
color='midnightblue'
/>
<Pressable onPress={() => console.log('Image pressed')}>
<Image
source={logoImage}
style={{ width: 300, height: 300 }}
/>
</Pressable>
<Pressable onPress={() => console.log('Text pressed')}>
<Text>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Qui eligendi perspiciatis ipsam
eaque modi, ducimus quae similique in at voluptatum cumque! Aliquam, quisquam id, placeat
neque a fugiat ea quam odio, repudiandae atque iste. Nulla at quaerat exercitationem
blanditiis aspernatur recusandae non laudantium omnis? Accusamus eaque mollitia optio?
Illo, aliquam! Repellat praesentium cum, earum iste iure ipsa accusamus, expedita,
temporibus optio corporis eius fugiat minus consequuntur aperiam est ullam nobis rerum.
Autem quibusdam unde molestiae? Similique repudiandae dolorum aut nobis hic aliquid
doloribus obcaecati beatae, fugit temporibus! Incidunt sint at ut id consectetur
necessitatibus, natus possimus sequi adipisci dignissimos minima accusantium ea ducimus
culpa libero assumenda! Hic doloremque alias placeat inventore quibusdam vitae neque
facere
</Text>
</Pressable>
</View>
)
}
- Now, let's return to the simulator and verify the code. When we press the Text component, we will see the corresponding log statement.
Custom Button using Pressable Component
If you feel that the native Button component doesn't meet your app's requirements, you can create a custom button using the Pressable component.
Just a heads up in case you want to build one from scratch.
Other Events Supported by Pressable Component
In addition to
onPress, the Pressable component supports several other events you can listen to:onPressInis called when a press is activated.onLongPressis triggered when a press is held for longer than 500 milliseconds.onPressOutis called when the press gesture is deactivated.Here is a great visualization from the React Native Docs:
After triggering
onPressIn, the user removes their finger, triggeringonPressOut, which is followed byonPress.However, if the user keeps their finger pressed for more than 500 milliseconds after
onPressIn,onLongPressis triggered followed byonPressOut.As an exercise, I encourage you to add these props to the Pressable component on the Image and handle the events to ensure they trigger as expected. Let me know in the comment section if they all work as expected.
=> Modal Component
Intro
Modal is a screen that overlays the app content to provide important information or prompt the user for a decision.
Since they are purposefully interruptive, make sure you use them only when necessary.
Show and Hide Modal on Button Press
To begin:
- Import the Modal component from React Native.
import React from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => console.log('Button pressed')}
color='midnightblue'
/>
</View>
)
}
Just before the closing View tag, define a Modal element.
The Modal opening and closing tags act as a container, and within it, we define a View that represents the content to be presented.
Duplicate the View component from above and modify the background color to light blue.
Inside the View component, add a Text component with the text "Modal content".
Add a button labeled "Close" with the color midnight blue.
import React from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => console.log('Button pressed')}
color='midnightblue'
/>
<Modal>
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 60 }}>
<Text>Modal content</Text>
<Button
title='Close'
color='midnightblue'
/>
</View>
</Modal>
</View>
)
}
Controlling Modal Visibility
By default, the Modal is always visible. We need to control its visibility based on user interaction.
To achieve this, we require a state variable. Import the
useStatehook from Reactand within the App component, create the state variable
isModalVisiblewith a setter functionsetIsModalVisible, and the default value for the state variable isfalse.
import React from 'react'
import { useState } from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
const [isModalVisible, setIsModalVisible] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => console.log('Button pressed')}
color='midnightblue'
/>
<Modal>
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 60 }}>
<Text>Modal content</Text>
<Button
title='Close'
color='midnightblue'
/>
</View>
</Modal>
</View>
)
}
- Bind this variable to the
visibleprop of the Modal component, sovisibleis equal toisModalVisible. Since it isfalseby default, the Modal is initially hidden.
import React from 'react'
import { useState } from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
const [isModalVisible, setIsModalVisible] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => console.log('Button pressed')}
color='midnightblue'
/>
<Modal visible={isModalVisible}>
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 60 }}>
<Text>Modal content</Text>
<Button
title='Close'
color='midnightblue'
/>
</View>
</Modal>
</View>
)
}
- To toggle the visibility of the Modal, modify the
onPressevent of the button component to call the setter functionsetIsModalVisiblepassing in a value oftrue.
import React from 'react'
import { useState } from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
const [isModalVisible, setIsModalVisible] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => setIsModalVisible(true)}
color='midnightblue'
/>
<Modal visible={isModalVisible}>
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 60 }}>
<Text>Modal content</Text>
<Button
title='Close'
color='midnightblue'
/>
</View>
</Modal>
</View>
)
}
- Add another event handler to the button within the Modal component to close it. So,
onPresscalls the functionsetIsModalVisibleand we pass infalseto hide the Modal.
import React from 'react'
import { useState } from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
const [isModalVisible, setIsModalVisible] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => setIsModalVisible(true)}
color='midnightblue'
/>
<Modal visible={isModalVisible}>
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 60 }}>
<Text>Modal content</Text>
<Button
title='Close'
onPress={() => setIsModalVisible(false)}
color='midnightblue'
/>
</View>
</Modal>
</View>
)
}
In the simulator, we have a view with a plum background and the "Press" button.
Click the button, and the Modal will be displayed.
Click the button within the Modal, and the Modal will be hidden.
Pretty straightforward usage.
Additional Props for Modal
- It is recommended to include another prop called
onRequestClosewhere we callsetIsModalVisiblepassing infalse.
import React from 'react'
import { useState } from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
const [isModalVisible, setIsModalVisible] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => setIsModalVisible(true)}
color='midnightblue'
/>
<Modal
visible={isModalVisible}
onRequestClose={() => setIsModalVisible(false)}>
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 60 }}>
<Text>Modal content</Text>
<Button
title='Close'
onPress={() => setIsModalVisible(false)}
color='midnightblue'
/>
</View>
</Modal>
</View>
)
}
The
onRequestClosehandler is triggered when the user presses the back button on Android or dismisses the Modal with a gesture on iOS.In either of the two scenarios, we want to hide the Modal.
Animation and Presentation Style Props
Let's explore a few more props related to Modals.
By default, the Modal abruptly appears and disappears because the
animationTypeprop is set tonone.
import React from 'react'
import { useState } from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
const [isModalVisible, setIsModalVisible] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => setIsModalVisible(true)}
color='midnightblue'
/>
<Modal
visible={isModalVisible}
onRequestClose={() => setIsModalVisible(false)}
animationType='none'>
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 60 }}>
<Text>Modal content</Text>
<Button
title='Close'
onPress={() => setIsModalVisible(false)}
color='midnightblue'
/>
</View>
</Modal>
</View>
)
}
- Set the prop to
slide, and the Modal will slide in from the bottom.
import React from 'react'
import { useState } from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
const [isModalVisible, setIsModalVisible] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => setIsModalVisible(true)}
color='midnightblue'
/>
<Modal
visible={isModalVisible}
onRequestClose={() => setIsModalVisible(false)}
animationType='slide'>
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 60 }}>
<Text>Modal content</Text>
<Button
title='Close'
onPress={() => setIsModalVisible(false)}
color='midnightblue'
/>
</View>
</Modal>
</View>
)
}
- Alternatively, set the value to
fade, and the Modal will fade into view.
import React from 'react'
import { useState } from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
const [isModalVisible, setIsModalVisible] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => setIsModalVisible(true)}
color='midnightblue'
/>
<Modal
visible={isModalVisible}
onRequestClose={() => setIsModalVisible(false)}
animationType='fade'>
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 60 }}>
<Text>Modal content</Text>
<Button
title='Close'
onPress={() => setIsModalVisible(false)}
color='midnightblue'
/>
</View>
</Modal>
</View>
)
}
- There is a prop called
presentationStyle, which is set tofullScreenby default.
import React from 'react'
import { useState } from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
const [isModalVisible, setIsModalVisible] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => setIsModalVisible(true)}
color='midnightblue'
/>
<Modal
visible={isModalVisible}
onRequestClose={() => setIsModalVisible(false)}
animationType='fade'
presentationStyle='fullScreen'>
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 60 }}>
<Text>Modal content</Text>
<Button
title='Close'
onPress={() => setIsModalVisible(false)}
color='midnightblue'
/>
</View>
</Modal>
</View>
)
}
- You can change this to either
formSheetorpageSheetto alter the appearance. For now, set it topageSheetand change back theanimationTypetoslideas it makes for a much better effect.
import React from 'react'
import { useState } from 'react'
import { View, Button, Text, Modal } from 'react-native'
export default function App() {
const [isModalVisible, setIsModalVisible] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Press'
onPress={() => setIsModalVisible(true)}
color='midnightblue'
/>
<Modal
visible={isModalVisible}
onRequestClose={() => setIsModalVisible(false)}
animationType='slide'
presentationStyle='pageSheet'>
<View style={{ flex: 1, backgroundColor: 'lightblue', padding: 60 }}>
<Text>Modal content</Text>
<Button
title='Close'
onPress={() => setIsModalVisible(false)}
color='midnightblue'
/>
</View>
</Modal>
</View>
)
}
- The
presentationStyleprop only affects iOS andnot Android.
Exercise: Implementing Modal Props
As an exercise, open an iPad simulator and toggle between
formSheetandpageSheetvalues for thepresentationStyleprop.Let me know in the comment section if you were successful in implementing it.
Summary
To summarize what we have learned:
The Modal component in React Native allows us to present important information or prompt user decisions.
You can define a Modal using the Modal component with the desired content nested as its children.
The visibility of the Modal can be controlled using the
visibleprop, and theonRequestClosecallback is used to close the Modal when it is dismissed through hardware interaction.We can use the
animationTypeprop to add fancier appearance transitions to the Modal, and thepresentationStyleprop in iOS to modify its presentation.
=> StatusBar component
Intro
Welcome back for our next component. Let's take a look at the status part component in React Native.
The status bar component provides control over the application's status bar, which is the area typically located at the top of the screen.
The status bar displays various information such as the current time, Wi-Fi and network status, battery level, and other status icons.
Now, our focus will be on the top section of the two devices. You can see they are transparent to begin with.
Code Example
let's now import the statusBar component
and within the view component invoke status bar
import React from 'react'
import { View, StatusBar } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<StatusBar />
</View>
)
}
- When we save the file, there is no visible change in iOS, but in Android, it adds a status bar component with a dark background.
Modifying Appearance
Let's now modify its appearance by adding some props:
- Set the background color using the backgroundColor prop. For example, backgroundColor: 'lightgreen' (specific to Android).
import React from 'react'
import { View, StatusBar } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<StatusBar backgroundColor='lightgreen' />
</View>
)
}
Please note that this prop is specific to Android. The iOS simulator has no background color, while the Android device shows a green background.
Although we can't change the background color on iOS, we can adjust the text color on both platforms.
The prop to control the text color is called
barStyle, and its default value isdefault.
import React from 'react'
import { View, StatusBar } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<StatusBar
backgroundColor='lightgreen'
barStyle='default'
/>
</View>
)
}
This corresponds to dark text on iOS and light text on Android.
Currently, we see black text on iOS and white text on Android.
We can change the value of barStyle to
dark-content, which results in black text on both platforms,or
light-content, which gives us white text on both platforms.
import React from 'react'
import { View, StatusBar } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<StatusBar
backgroundColor='lightgreen'
barStyle='dark-content'
/>
</View>
)
}
- Depending on your application's background color, choose the appropriate value to ensure good visibility and contrast.
Visibility and Hidden Prop
- Speaking of visibility, you can use the
hiddenprop to hide the status bar.
import React from 'react'
import { View, StatusBar } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<StatusBar
backgroundColor='lightgreen'
barStyle='dark-content'
hidden
/>
</View>
)
}
Add the hidden prop, and you'll notice that the status bar is now hidden.
As an exercise, I encourage you to try toggling the visibility of the status bar by using a state variable and a button click event, similar to how you control the visibility of a modal.
import React from 'react'
import { useState } from 'react'
import { View, StatusBar, Button, Text } from 'react-native'
export default function App() {
const [isStatusBarHidden, setIsStatusBarHidden] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<StatusBar
backgroundColor='lightgreen'
barStyle='dark-content'
hidden={isStatusBarHidden}
/>
<Button
title='Hidden'
onPress={() => setIsStatusBarHidden(true)}
color='midnightblue'
/>
<Text> </Text>
<Button
title='Visible'
onPress={() => setIsStatusBarHidden(false)}
color='midnightblue'
/>
</View>
)
}
Summary
To summarize, the status bar component allows you to control the application's status bar.
You can adjust the background color on Android using the backgroundColor prop,
change the text color using the barStyle prop,
and toggle visibility using the hidden prop.
=> ActivityIndicator Component
Intro
In this video, let's take a look at the ActivityIndicator component.
The ActivityIndicator component displays a circular loading indicator.
It is used to inform users about the status of ongoing processes, such as loading an app, submitting a form, or saving updates.
Usage
- To begin, import the ActivityIndicator component from React Native and place it within a view.
import React from 'react'
import { useState } from 'react'
import { View, ActivityIndicator } from 'react-native'
export default function App() {
const [isStatusBarHidden, setIsStatusBarHidden] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<ActivityIndicator />
</View>
)
}
If we save the file, you will notice the circular loading indicator in both iOS and Android.
They are indeed very small, and the color contrast is not great. So, I recommend you verify this on your own simulator or virtual device.
Although they have slight visual differences, they effectively convey the message that something is happening in the background.
Important Props
Let's highlight three important props for this component:
- The
sizeprop: By default, it is set tosmall, but we can change it tolarge.
- The
import React from 'react'
import { useState } from 'react'
import { View, ActivityIndicator } from 'react-native'
export default function App() {
const [isStatusBarHidden, setIsStatusBarHidden] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<ActivityIndicator />
<ActivityIndicator size='large' />
</View>
)
}
- The
colorprop: By default, it uses the system accent default color for Android and a shade of gray for iOS. However, we can customize it by setting a specific color, such asmidnightblue.
import React from 'react'
import { useState } from 'react'
import { View, ActivityIndicator } from 'react-native'
export default function App() {
const [isStatusBarHidden, setIsStatusBarHidden] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<ActivityIndicator />
<ActivityIndicator size='large' />
<ActivityIndicator
size='large'
color='midnightblue'
/>
</View>
)
}
The
animatingprop: By default, it is set totrue, which is why we see the indicator.We can set it to
falseto hide the indicator.
import React from 'react'
import { useState } from 'react'
import { View, ActivityIndicator } from 'react-native'
export default function App() {
const [isStatusBarHidden, setIsStatusBarHidden] = useState(false)
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<ActivityIndicator />
<ActivityIndicator size='large' />
<ActivityIndicator
size='large'
color='midnightblue'
/>
<ActivityIndicator
size='large'
color='midnightblue'
animating={false}
/>
</View>
)
}
Keep in mind that the recommended approach is to use a state variable for the
animatingprop.In summary, the ActivityIndicator component displays a loading indicator to inform users about ongoing background processes.
You can customize it using the
sizeprop to set the size,the
colorprop to set a predefined or custom color,and the
animatingprop to control visibility.
=> Alert component
Intro
The Alert component in React Native serves more as an API than a typical component. Instead of rendering it as part of JSX, you invoke methods that generate UI elements.
When using the Alert component, you can launch another dialog with a specified title and message.
Additionally, you have the option to include a list of buttons.
Usage Example
Import the Alert component from React Native.
Define a button within the View component in the JSX of the App component.
Import the Button component.
Add a title to the Button component, called "alert", and an onPress prop.
The onPress prop should be an arrow function where we invoke
Alert.alert(), passing a string ("invalid data") as the first parameter.
import React from 'react'
import { View, Alert, Button } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Alert'
onPress={() => Alert.alert('Invalid data!')}
/>
</View>
)
}
- Upon pressing the button, the alert dialog will be displayed with the specified
title("invalid data").
Adding a massage
Optionally, you can specify a
messageas the second parameter in theAlert.alert()function.To display multiple alerts, duplicate the button and change the title and message accordingly.
import React from 'react'
import { View, Alert, Button } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Alert'
onPress={() => Alert.alert('Invalid data!')}
/>
<Button
title='Alert 2'
onPress={() => Alert.alert('Invalid data!', 'DOB Incorrect')}
/>
</View>
)
}
Controlling the buttons
- on iOS you can specify any number of buttons while on Android the limit is three
To control the buttons shown in the alert, you can pass an array as the third parameter in the
Alert.alert()function.Within the array, specify objects with a "text" property (e.g., "cancel") and a corresponding "onPress" handler.
The "onPress" handler can be defined as an arrow function that performs a desired action (e.g., logging to the console).
import React from 'react'
import { View, Alert, Button } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Button
title='Alert'
onPress={() => Alert.alert('Invalid data!')}
/>
<Button
title='Alert 2'
onPress={() => Alert.alert('Invalid data!', 'DOB Incorrect')}
/>
<Button
title='Alert 3'
onPress={() =>
Alert.alert('Invalid data!', 'DOB Incorrect', [
{
text: 'Cancel',
onPress: () => console.log('Cancel pressed'),
},
{
text: 'OK',
onPress: () => console.log('OK pressed'),
},
])
}
/>
</View>
)
}
Available APIs
- Alert is just one of the many available apis in react native
The appearance of the alert dialog may vary slightly between iOS and Android platforms.
For a comprehensive list of available APIs in React Native, refer to the documentation on development APIs.
=> Custom Components
Intro
- In React Native, we often combine core components to create custom user interfaces.
Create a custom component
To create a custom component, start by creating a new folder called "components" within your project folder.
- Inside the "components" folder, create a new file called "Greet.js".
In "Greet.js", define a basic component that accepts a "name" prop and renders JSX.
Import the "View" and "Text" components from React Native.
Export a default function called "Greet" that accepts a "name" prop.
Within the function, destructure the "name" prop and return JSX with a "View" component and a nested "Text" component.
The "Text" component will display "Hello" followed by the 'name' passed into the component.
import { View, Text } from 'react-native'
export default function Greet({ name }) {
return (
<View>
<Text>Hello, {name}</Text>
</View>
)
}
- Import the newly created "Greet" component into "App.js".
import React from 'react'
import { View } from 'react-native'
import Greet from './components/Greet'
export default function App() {
return <View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}></View>
}
- Use the "Greet" component with a "name" prop, such as "Bruce Wayne" or "Clark Kent".
import React from 'react'
import { View } from 'react-native'
import Greet from './components/Greet'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Greet name='Bruce Wane' />
<Greet name='Clark Kent' />
</View>
)
}
Summary
By combining core components like "View" and "Text", we can create reusable custom components.
Custom components can include components like cards, buttons, list items, and more.
The essence of building React Native apps is combining core components to create reusable custom components that form user interfaces.
Next, we will focus on styling in React Native and explore the StyleSheet API.
Section 3: Styling
=> Styling React Native apps
Intro
Styling React Native apps shares similarities with styling web apps but introduces some key differences.
The most notable difference is that
React Native does not use CSS for styling.Instead, you style your app using JavaScript itself.
The naming conventions for styles and their values are similar to CSS on the web but with a slight modification.
Names are written in camel case, such asbackgroundColorinstead ofbackground-color.
Styling Approaches
The first approachis using inline styles with thestyle prop, which is accepted by most of the core components.We have already explored this approach in the previous section of this course, where we specified a plain JavaScript object as the value for the
styleprop.The second approachinvolves utilizing thestylesheet APIprovided by React Native.This API allows you to define multiple styles in one place using the
createmethod.
The stylesheet API
In this section,
we will primarily focus on the stylesheet API.We will explore how to style various core components and understand the nuances of applying specific styles across iOS and Android platforms.
=> The StyleSheet API
Intro
In this video, we will explore the StyleSheet API which allows us to style components in React Native.
For this section on styling, we have created a new Expo project called
StylingRNHowever, you can continue working on the same core components project from the previous section if you prefer to.
Just ensure that you start with an empty
app.jsfile to follow along.
Using inline styles
Let's start by quickly creating a component that will render some text at the top:
On the
Viewcomponent, we add thestyleprop withflexset to1so as to occupy the entire available space, abackgroundColor, and finallypadding.import React from 'react'
import { View, Text } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Text>StyleSheet API</Text>
</View>
)
}
Using the StyleSheet API
If we save the file and take a look at the two devices, we can see the expected UI.
This approach uses inline styling which we have already come across. Although inline styles work fine, they are not the recommended approach for styling in React Native.
In fact, you will rarely find them in a codebase.
The preferred method is to use the StyleSheet API.
Let me demonstrate how to use it by replacing the inline style.
To begin, import the StyleSheet API from React Native:
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Text>StyleSheet API</Text>
</View>
)
}
- Next, below the
Appcomponent, call thecreatemethod on the StyleSheet API, and assign the returned value to a constant calledstyles:
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum', padding: 60 }}>
<Text>StyleSheet API</Text>
</View>
)
}
const styles = StyleSheet.create({})
As an argument to the
createmethod, provide an object that contains key-value pairs.You can choose any key name you want to, but it is recommended to give them semantic meaning.
For example, I will limit
container.The value should be an object containing key-value pairs similar to CSS properties and values but in JavaScript.
In our case, we can extract the styles from the
styleprop and assign them to thecontainerkey.So cut the object and specify as the value to
container.Now, for the
styleprop on theViewcomponent, specifystyles.container.
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<Text>StyleSheet API</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
})
- If we save the file, we can see that the UI remains the same as before, but this time we have styled our component using the StyleSheet API.
why use StyleSheet API over inline styles
Now, there are a couple of reasons:
First, by moving styles away from the render function, you
make the code easier to read and understand.Second, naming the styles
adds meaning to the low-level components in the render function.For example, you could create a
titlestyle and assign it to theTextcomponent asstyles.title.
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<Text style={style.title}>StyleSheet API</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
title: {},
})
This makes it clear that the
Viewrepresents the container and theTextrepresents the title for the screen.Finally, organizing styles in this manner
makes the code reusable and easier to maintain.Imagine having the same inline styles across multiple
Textcomponents and you need to change the font size.You would have to update each component style individually.
However, by grouping the styles using the StyleSheet API, you only need to change it once as the updated
titlestyle will be reflected across all components.It is also important to note that these styles can only be used within the same file.
However, you do have the option to create a separate Global Styles file and export the styles object.
export const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
title: {},
})
You can then use it in any file throughout your codebase.
This allows for reusing the styles across different components and promoting consistency in your application's visual design.
Creating a style object without using the StyleSheet API
Well, in that case, you won't have access to autocomplete suggestions.
If I try to add a margin, you can see we don't have any autocomplete.
But the same with the StyleSheet API, we have access to those autocomplete suggestions.
And trust me, in React Native, having the assistance is valuable since the property names are quite different from what you're used to in the browser.
All right, I hope you now have a clear understanding of the two methods of styling React Native components: inline styles and the StyleSheet API. The StyleSheet API is the recommended approach as it promotes writing organized, easy-to-read, and maintainable code.
=> Multiple Styles
Intro
- in the previous video we learned about Styling with Stylesheet API
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<Text style={styles.title}>StyleSheet API</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
title: {},
})
- The Stylesheet API is used for styling in React Native.
Applying Multiple Styles to a Component
To begin, we'll add two boxes within our container View component, each with text:
Light blue box
Light green box
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View>
<Text>Lightblue box</Text>
</View>
<View>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
title: {},
})
If we look at the two devices, we can see the text from the two boxes, but they don't resemble a box in any way. Let's add some styles to fix this.
Inside the Stylesheet API object, below the container key, we'll add two more keys:
Light blue box: An object that sets the background color to light blue, with a height of 100 and padding of 10.
Light green box: An object that sets the background color to light green.
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View>
<Text>Lightblue box</Text>
</View>
<View>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
lightBlueBox: {
backgroundColor: 'lightblue',
width: 100,
height: 100,
padding: 10,
},
lightGreenBox: {
backgroundColor: 'lightgreen',
width: 100,
height: 100,
padding: 10,
},
})
Now, we can use these styles for our View components.
On the first nested View component, add the style prop and set it to
Styles.lightBlueBox.Repeat the process for the light green box by setting the style prop to
Styles.lightGreenBox.
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.lightBlueBox}>
<Text>Lightblue box</Text>
</View>
<View style={styles.lightGreenBox}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
lightBlueBox: {
backgroundColor: 'lightblue',
width: 100,
height: 100,
padding: 10,
},
lightGreenBox: {
backgroundColor: 'lightgreen',
width: 100,
height: 100,
padding: 10,
},
})
If we take a look at the UI, we can see that the two boxes are now displayed as expected.
Everything works great, but there is room for improvement.
Extracting and Reusing Shared Styles
There are common styles shared between the light blue box and the light green box.
We can extract these shared styles and reuse them in React Native.
Let's refactor the styles:
Start with a key called "box" and extract width, height, and padding into this new box.
Change "light blue box" to "light blue background" and set the background color to light blue.
Similarly, change "light green box" to "light green background" and set the background color to light green.
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.lightBlueBox}>
<Text>Lightblue box</Text>
</View>
<View style={styles.lightGreenBox}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 100,
height: 100,
padding: 10,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
})
Now, we have separated the common styles from the colors.
The light blue box is a combination of "box" with "light blue background", while the light green box is a combination of "box" and "light green background".
specify multiple styles for a component
To specify multiple styles for a component, we use an array of styles.
For the light blue box, we use an array as the value to the style prop and specify
styles.box, styles.lightBlueBg.Similarly, for the light green box, we specify an array
styles.box, styles.lightGreenBg.
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={[styles.box, styles.lightBlueBg]}>
<Text>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 100,
height: 100,
padding: 10,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
})
If we now take a look at the two devices, we can see that the two boxes are still rendering as expected (light blue and light green).
The styles, however, are written in a much more organized manner.
the last style in the array takes precedence
One important point to note is that when merging different styles, the last style in the array takes
precedence.Even if we add a background color "pink" to the "box" style,
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 100,
height: 100,
padding: 10,
backgroundColor: 'pink',
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
})
the boxes will continue to render with their respective colors.
However, if we change the order in the array so that
Styles.boxis the last element,
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View>
<Text style={[styles.lightBlueBg, styles.box]}>Lightblue box</Text>
</View>
<View>
<Text style={[styles.box, styles.lightGreenBg]}>Lightgreen box</Text>
</View>
</View>
)
}
we can see that the box color is now pink instead of light blue.
When merging the background color from the two styles, the value from the last element in the array takes precedence.
To summarize
You can specify multiple styles using arrays in tags.
When merging styles, the value from the last style in the array takes precedence.
=> Box Model
Intro
In this video, let's take a look at the different styles applicable in relation to the CSS Box model.
As you may already know,
the CSS Box model represents a box that encloses every HTML element consisting of margins, borders, padding, and the actual content.Interestingly,
these properties are also applicable to React Native components.Let's delve into the specifics of these properties in this video.
Height and Width Properties
To begin, let's discuss the height and width properties.
These properties have already been covered in the previous videos of the series.
They're used to define the dimensions of a component.
It is important to note that
in React Native, all dimensions are unitlessand represent density independent pixels.No specific units like Rems or pixels are required.
In our example, both the width and height are set to 100.
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View>
<Text style={[styles.lightBlueBg, styles.box]}>Lightblue box</Text>
</View>
<View>
<Text style={[styles.box, styles.lightGreenBg]}>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 100,
height: 100,
padding: 10,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
})
But what you can do is specify percentage values.
when the parent's dimensions are defined. Child components can have percentage dimensions.In this case, since the container has a flex value of 1, occupying the entire available screen space, we can set the Box's width and height to 25 percent.
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View>
<Text style={[styles.lightBlueBg, styles.box]}>Lightblue box</Text>
</View>
<View>
<Text style={[styles.box, styles.lightGreenBg]}>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: '25%',
height: '25%',
padding: 10,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
})
As a result, you can observe that the Box width and height now account for 25 percent of the apparent container's width and height, respectively.
Together, the two boxes occupy fifty percent of the available height and 25 percent of the available width.
So this covers the width and height aspects of the element.
Padding
Moving on, let's talk about padding.
Similar to CSS for the web,
React Native allows us to specify padding in all four directions.Additionally, there are a few properties in React Native that you should be aware of.
You can individually set padding in the horizontal and vertical directions using
paddingHorizontalandpaddingVertical.So, let's comment out padding and instead add
paddingHorizontal: 10andpaddingVertical: 20. Take a look at the UI,
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={[styles.box, styles.lightBlueBg]}>
<Text>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 100,
height: 100,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
})
- and you will see the styles work as expected.
Margin
The margin property also functions similarly to its CSS counterpart, and in React Native, we can utilize
marginHorizontalandmarginVerticalas well.So,
margin: 10works completely fine, but we can also havemarginVertical: 10. Take a look at the UI,
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={[styles.box, styles.lightBlueBg]}>
<Text>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 100,
height: 100,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
})
- and we now clearly see the spacing between the two boxes because of the vertical margin.
Borders
Next, let's take a look at borders in React Native.
In traditional web CSS, we often specify the border with a shorthand property like
border: 2px solid purple.However, this approach does not work in React Native.
Instead, we need to specify the properties individually.
First, we set
borderWidthto 2. Next, we haveborderColor, which is a string "purple". Finally, we haveborderStyle, which is set to "solid" by default, so we don't need to explicitly mention it.
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={[styles.box, styles.lightBlueBg]}>
<Text>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 100,
height: 100,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
borderWidth: 2,
borderColor: 'purple',
// borderStyle: 'solid'
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
})
- Take a look at the simulator, and you will see the purple border applied to both boxes.
Border Radius
Finally, I want to highlight a nuance regarding border radius in iOS versus Android.
If we set a border radius on a text component, let's go with inline styles for now:
borderRadius: 5and this needs a background color, sobackgroundColor: "red",
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={[styles.box, styles.lightBlueBg]}>
<Text style={{ borderRadius: 5, backgroundColor: 'red' }}>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 100,
height: 100,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
borderWidth: 2,
borderColor: 'purple',
// borderStyle: 'solid'
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
})
you will notice that it only applies to Android and not iOS.
We have the curved border in Android but not iOS.
However, if we apply
borderRadiusto the view component, soborderRadius: 5,
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={[styles.box, styles.lightBlueBg]}>
<Text style={{ borderRadius: 5, backgroundColor: 'red' }}>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 100,
height: 100,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
borderWidth: 2,
borderColor: 'purple',
// borderStyle: 'solid'
borderRadius: 5,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
})
you will observe that the boxes have rounded corners on both platforms.
This is an important distinction to keep in mind.
Border radius is applicable to the view component across both platforms, but for the text component, it only applies to Android.If you wish to add a border radius to text,
the solution is to wrap it with a view component and apply the border radius to the view component instead.
I hope you are able to slowly recognize the differences in styling between the web and React Native, as well as the variations in styling for iOS and Android platforms.
=> Shadow and Elevation
Intro
In the previous video, we learned about the differences in styling between web and React Native when it comes to some Box model properties.
We also understood the nuances of applying the Border radius property for iOS and Android.
Box Shadows in CSS
Applying box shadows using CSS for the web is straightforward.
We specify the horizontal offset, vertical offset, blur, spread, and color.
Box Shadows in React Native
In React Native, we use a group of four properties.
Let's add a new key called
boxShadowto the Styles object and specify the different shadow properties.The first property is
shadowColor, which determines the color of the box shadow. Let's set it to#333333.The second property is
shadowOffset, which accepts an object containing width and height properties with numerical values. Let's set width to 6 and height to 6.The third property is
shadowOpacity, which sets the transparency of the box shadow. Its value ranges from 0 to 1, with 0 representing complete transparency and 1 representing complete opacity. Let's setshadowOpacityto 0.6 for our example.The fourth property is
shadowRadius, which accepts a number to set the blur radius. A larger value creates a larger and lighter blur, making the shadow more prominent. Let's set it to 4.Let's also change the box width and height to 250.
Next, we need to specify the box shadow in the Styles array for the light blue box.
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<Text style={[styles.box, styles.lightBlueBg, styles.boxShadow]}>
<Text style={{ borderRadius: 5, backgroundColor: 'red' }}>Lightblue box</Text>
</View>
<Text style={[styles.box, styles.lightGreenBg]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 250,
height: 250,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
borderWidth: 2,
borderColor: 'purple',
// borderStyle: 'solid'
borderRadius: 5,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
boxShadow: {
shadowColor: '333333',
shadowOffset: { width: 6, height: 6 },
shadowOpacity: 0.6,
shadowRadius: 4,
},
})
When we take a look at the iOS simulator, we can see that the box shadow is applied.
However, if we take a look at Android, there is no box shadow.
the elevation property
There are no common styles to apply shadows in both iOS and Android.
To add box shadows in Android, we have to use the
elevationproperty, which internally utilizes the Android elevation API.Let's add a new key to our Styles object called
androidShadowwith a propertyelevationset to 10, and let's apply this to the light green box.
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={[styles.box, styles.lightBlueBg, styles.boxShadow]}>
<Text style={{ borderRadius: 5, backgroundColor: 'red' }}>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg, styles.androidShadow]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 250,
height: 250,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
borderWidth: 2,
borderColor: 'purple',
// borderStyle: 'solid'
borderRadius: 5,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
boxShadow: {
shadowColor: '333333',
shadowOffset: { width: 6, height: 6 },
shadowOpacity: 0.6,
shadowRadius: 4,
},
androidShadow: {
elevation: 10,
},
})
- When we view the Android device, we can see the shadow on the light green box.
shadowColor is the only property that works on both iOS and Android
It's also worth mentioning that
shadowColoris the only property that works on both iOS and Android.As an exercise, I encourage you to change the shadow color to
blueand apply it to both iOS and Android. Let me know in the comment section if it works for you.
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={[styles.box, styles.lightBlueBg, styles.boxShadow]}>
<Text style={{ borderRadius: 5, backgroundColor: 'red' }}>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg, styles.androidShadow]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 250,
height: 250,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
borderWidth: 2,
borderColor: 'purple',
// borderStyle: 'solid'
borderRadius: 5,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
boxShadow: {
shadowColor: 'blue',
shadowOffset: { width: 6, height: 6 },
shadowOpacity: 0.6,
shadowRadius: 4,
},
androidShadow: {
elevation: 20,
shadowColor: 'blue',
},
})
Conclusion
Shadow properties do not work in Android, and you have to rely on the
elevationproperty.There are packages available that support cross-platform shadows, but that is not the topic we want to address right now.
=> Style Inheritance
Intro
In this final video of the section, we will explore style inheritance in React Native.
Let's dive straight into the code and look at an example to better understand this concept.
Within our
Appcomponent, within the outerViewcomponent, let's add a newViewand nest aTextcomponent inside it.The text is going to read "Style Inheritance".
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View>
<Text>Style inheritance</Text>
</View>
<View style={[styles.box, styles.lightBlueBg, styles.boxShadow]}>
<Text style={{ borderRadius: 5, backgroundColor: 'red' }}>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg, styles.androidShadow]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
box: {
width: 250,
height: 250,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
borderWidth: 2,
borderColor: 'purple',
// borderStyle: 'solid'
borderRadius: 5,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
// boxShadow: {
// shadowColor: 'blue',
// shadowOffset: { width: 6, height: 6 },
// shadowOpacity: 0.6,
// shadowRadius: 4,
// },
androidShadow: {
elevation: 20,
shadowColor: 'blue',
},
})
- If we return to the UI, we can see the text appears in black.
Defining Styles for the Wrapping View Component
Now, let's define some styles for the wrapping
Viewcomponent.In
styles.create, let's add a new key calleddarkModeBackgroundColorand set it to black.On the new
Viewcomponent,styleis equal toStyles.darkModeBackgroundColor.
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.darkMode}>
<Text>Style inheritance</Text>
</View>
<View style={[styles.box, styles.lightBlueBg, styles.boxShadow]}>
<Text style={{ borderRadius: 5, backgroundColor: 'red' }}>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg, styles.androidShadow]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
darkMode: {
backgroundColor: 'black',
},
box: {
width: 250,
height: 250,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
borderWidth: 2,
borderColor: 'purple',
// borderStyle: 'solid'
borderRadius: 5,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
// boxShadow: {
// shadowColor: 'blue',
// shadowOffset: { width: 6, height: 6 },
// shadowOpacity: 0.6,
// shadowRadius: 4,
// },
androidShadow: {
elevation: 20,
shadowColor: 'blue',
},
})
- If you now take a look at the UI, we see the
Viewwith the dark background. However, the text is not visible since its default color is also black.
Adding Font Color
Let's go back to our styles and add a font color for dark mode.
backgroundColor: 'black',color: 'white',
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.darkMode}>
<Text>Style inheritance</Text>
</View>
<View style={[styles.box, styles.lightBlueBg, styles.boxShadow]}>
<Text style={{ borderRadius: 5, backgroundColor: 'red' }}>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg, styles.androidShadow]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
darkMode: {
backgroundColor: 'black',
color: 'white',
},
box: {
width: 250,
height: 250,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
borderWidth: 2,
borderColor: 'purple',
// borderStyle: 'solid'
borderRadius: 5,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
// boxShadow: {
// shadowColor: 'blue',
// shadowOffset: { width: 6, height: 6 },
// shadowOpacity: 0.6,
// shadowRadius: 4,
// },
androidShadow: {
elevation: 20,
shadowColor: 'blue',
},
})
If we save the file and go back to the UI, you can see that we still don't see the text.
In CSS for the web, setting a font color on a
divtag applies the same color to nestedparagraphtags within thedivtag. This is possible due to inheritance, which is a feature in CSS.However,
when working with styles in React Native, there is no inheritance of styles from aViewcomponent to aTextcomponent.To set the text color to white, you need to create and apply a separate style on this text component.
For example,
darkModeText: { color: 'white', },And apply on the text component:
style={Styles.darkModeText}
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.darkMode}>
<Text style={styles.darkModeText}>Style inheritance</Text>
</View>
<View style={[styles.box, styles.lightBlueBg, styles.boxShadow]}>
<Text style={{ borderRadius: 5, backgroundColor: 'red' }}>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg, styles.androidShadow]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
darkMode: {
backgroundColor: 'black',
},
darkModeText: {
color: 'white',
},
box: {
width: 250,
height: 250,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
borderWidth: 2,
borderColor: 'purple',
// borderStyle: 'solid'
borderRadius: 5,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
// boxShadow: {
// shadowColor: 'blue',
// shadowOffset: { width: 6, height: 6 },
// shadowOpacity: 0.6,
// shadowRadius: 4,
// },
androidShadow: {
elevation: 20,
shadowColor: 'blue',
},
})
- If we return to the UI, you will see that the text now appears in white.
Understanding Bold Text Style Inheritance
Now, let's add a new style called
boldTextand setfontWeightto bold.In the JSX, within the
Textcomponent right after "Style Inheritance", I'm going to nest anotherTextcomponent.The text is going to read "in bold", and for the style, I'm going to apply only the
boldTextstyle:style={Styles.boldText}
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.darkMode}>
<Text style={styles.darkModeText}>
Style inheritance<Text style={styles.boldText}> in bold</Text>
</Text>
</View>
<View style={[styles.box, styles.lightBlueBg, styles.boxShadow]}>
<Text style={{ borderRadius: 5, backgroundColor: 'red' }}>Lightblue box</Text>
</View>
<View style={[styles.box, styles.lightGreenBg, styles.androidShadow]}>
<Text>Lightgreen box</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
padding: 60,
},
darkMode: {
backgroundColor: 'black',
},
darkModeText: {
color: 'white',
},
boldText: {
fontWeight: 'bold',
},
box: {
width: 250,
height: 250,
// width: '25%',
// height: '25%',
// padding: 10,
paddingHorizontal: 10,
paddingVertical: 20,
// margin: 10,
marginVertical: 10,
borderWidth: 2,
borderColor: 'purple',
// borderStyle: 'solid'
borderRadius: 5,
},
lightBlueBg: {
backgroundColor: 'lightblue',
},
lightGreenBg: {
backgroundColor: 'lightgreen',
},
// boxShadow: {
// shadowColor: 'blue',
// shadowOffset: { width: 6, height: 6 },
// shadowOpacity: 0.6,
// shadowRadius: 4,
// },
androidShadow: {
elevation: 20,
shadowColor: 'blue',
},
})
Now, here is my question: Is the bold text styled in white and bold, or just bold? Take a moment to think about it and let me know your answer in the comment section.
If we return to the UI, we can see that the bold text is indeed both bold and white.
Although React Native's style inheritance capabilities are limited compared to CSS, it still supports style inheritance within text subtrees.In our case, the white color from the parent
Textcomponent is inherited by the nestedTextcomponent.So, inheritance fromViewtoTextdoes not work, but fromTextto another nestedTextdoes work.
Conclusion
With that, we conclude this section on styling in React Native.
We have seen how to add styles inline and using the stylesheet API,
how to add multiple styles to the same component using the array syntax,
the differences in styling Box model properties for the web versus React Native and for iOS versus Android.
We also had a look at shadows and elevation,
and finally, style inheritance.
In the next section, let's focus our attention on understanding layouts in React Native.
Section 4: Layout
=> Layout with Flexbox
Intro
In this section, we will dive into the world of layouts in React Native.
The core of layout design in React Native is flexbox, a powerful one-dimensional layout model used to arrange elements within a container.
With flexbox, you have the freedom to arrange items either horizontally (left to right or right to left) or vertically (top to bottom or bottom to top).
Additionally, you can easily control the spacing and alignment of items within the container.
Understanding Flexbox:
Flexbox consists of two main entities: the flex container and the flex items.
The parent container, usually a View component, is referred to as the
flex container.The immediate children elements are called
flex items.
<View>
<View>Item 1</View>
<View>Item 2</View>
<View>Item 3</View>
</View>
- In the provided sample UI, the parent view acts as the flex container, and all the children views serve as flex items.
The Axes in Flexbox:
When working with flexbox, we encounter two axes: the
main axisand thecross axis.By default,
the main axis runs from left to right,and the cross axis is perpendicular to it,running from top to bottom.
However, in React Native, it is the opposite:
the main axis runs top to bottom,and the cross axis runs left to right.
It is very important to remember this.
Understanding Flexbox Properties:
Understanding flexbox is a straightforward process, much like learning CSS.
You need to familiarize yourself with the various flexbox properties and understand how they function.
Once you grasp the concepts of flexbox, you'll have a solid understanding of how layouts are created in React Native.
In this section, we will focus on exploring different flexbox properties while arranging elements on the screen.
=> Code Setup
Intro
In this video, we will set up the initial code that we will be working with throughout this section.
For the section on layouts, we have created a new Expo project called RNLayout
npx create-expo-app RNLayout
Step 1: Creating a Reusable Box Component:
Within the project folder, create a new folder called "
components."Inside the "components" folder, create a new file called "
Box.js."Define the Box custom component that will utilize the View and Text core components along with the StyleSheet API for styling.
Start by importing the required components and APIs (View, Text, and StyleSheet from React Native).
Next, default export a function called "box."
For the JSX, return the View component with the Text component nested inside to display the text box.
import { View, Text, StyleSheet } from 'react-native'
export default function Box() {
return (
<View>
<Text>Box</Text>
</View>
)
}
Next, add the styles to give this component the appearance of a box.
The const Styles is equal to StyleSheet.create, and we pass in an object with a key called "box" that has a background color of white and a padding of 20 pixels.
Also, add another key called "text" with font size 24, font weight bold, and text align center.
import { View, Text, StyleSheet } from 'react-native'
export default function Box() {
return (
<View>
<Text>Box</Text>
</View>
)
}
const styles = StyleSheet.create({
box: {
backgroundColor: '#fff',
padding: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
})
- On the View component, specify style is equal to Styles.box, and on the Text component, style is equal to Styles.text.
import { View, Text, StyleSheet } from 'react-native'
export default function Box() {
return (
<View style={styles.box}>
<Text style={styles.text}>Box</Text>
</View>
)
}
const styles = StyleSheet.create({
box: {
backgroundColor: '#fff',
padding: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
})
- With this, our Box component is now ready.
Step 2: Defining the Flex Container in App.js:
Let's begin by making some changes in App.js.
Remove the StatusBar import, the Text component import, and clear the contents within the View component in the JSX.
As for the container Styles, remove all the properties.
Finally, import the Box component and include it in the JSX.
import { StyleSheet, View } from 'react-native'
export default function App() {
return <View style={styles.container}></View>
}
const styles = StyleSheet.create({
container: {},
})
If you now take a look at the devices, we see our Box component on an Android device but not iPhone, and this is because
the Box text is hidden behind the notch.To fix this, on the container Styles, add a top margin of 64.
marginTop: '64',
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box></Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
marginTop: 64,
},
})
We should now see the Box text on both the devices.
However, the UI doesn't look appealing, and it is difficult to identify where the container is and where the box is.
Step 3: Adding Props to the Box Component:
- Let's add two props to the Box component: children and style.
import { View, Text, StyleSheet } from 'react-native'
export default function Box({ children, style }) {
return (
<View style={styles.box}>
<Text style={styles.text}>Box</Text>
</View>
)
}
const styles = StyleSheet.create({
box: {
backgroundColor: '#fff',
padding: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
})
The children prop allows the parent to control the text rendered inside the box, and the style prop allows the parent component to extend the Styles applied to the Box component.
Now, instead of rendering the text box, let's render the children prop.
On the View component, let's specify multiple styles using the array syntax, so Styles.box, comma Style.
Also, add a white font color to the Text component.
import { View, Text, StyleSheet } from 'react-native'
export default function Box({ children, style }) {
return (
<View style={[styles.box, style]}>
<Text style={styles.text}>{children}</Text>
</View>
)
}
const styles = StyleSheet.create({
box: {
backgroundColor: '#fff',
padding: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
color: 'white',
},
})
Back in App.js, add seven items within the container.
Copy and paste the text box 1, Box 2, and so on, representing the children prop, and the background color is what we pass to the style prop.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
marginTop: 64,
},
})
- These will get merged with the box styles in Box.styles.
Final Step: Adding a Border to the Container:
I'm going to add a border to clearly identify the container at any given point.
Border width 6 and border color red.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
If you now go back to the devices, we can clearly see our container in red with the seven items nested inside with different colors.
We are now ready to start learning about the different properties in flexbox.
=> flex
Intro
The flex property plays a crucial role in defining how much of a view will fill the screen along the main axis.
It accepts an integer value greater than or equal to zero indicating the fraction of the available space the component should occupy.
In react native The View component is automatically set to display Flex by default
so in our case the view container is already a flex container and the boxes inside are considered Flex items
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
=>
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Example 1
Replace the entire View component with a simple View that has an inline Style.
Set the background color of the View component to Plum.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={{ backgroundColor: 'plum' }}></View>
// <View style={styles.container}>
// <Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
// <Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
// <Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
// <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
// <Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
// <Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
// <Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
// </View>
)
}
const styles = StyleSheet.create({
container: {
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
The View component only occupies enough space to accommodate its children by default.
To make the View occupy the entire available space, add the flex property and set it to a value of 1.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: 'plum' }}></View>
// <View style={styles.container}>
// <Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
// <Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
// <Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
// <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
// <Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
// <Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
// <Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
// </View>
)
}
const styles = StyleSheet.create({
container: {
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
we can see the plum colored background now occupies the entire available space
and this is a common approach in react native apps
adding a view component and setting the flex property to 1 to occupy all available space
Example 2
- for the second example let's work with the code that we set up in the previous video
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
if we save the file and take a look at the devices, we can see the space occupied by the flex container it is indicated by the red border
To ensure the container occupies the entire available space, set the flex property of the container to 1.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Example 3
The flex property is not limited to the flex container; it is also applicable to flex items.
Add Flex 1 to the first box component.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ flex: 1, backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
The first box now occupies all the available space within the container.
The flex value indicates the fraction of available space the component should take up.
When Flex 1 is set on the second box,
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ flex: 1, backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ flex: 1, backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
both box 1 and box 2 equally share the available space between them, taking up 50 percent of the available space each.
When Flex 1 is set on the third box,
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ flex: 1, backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ flex: 1, backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ flex: 1, backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- the first three boxes each take up 33 percent of the available space.
Example 4
- Remove the flex property from the third box and instead set Flex 3 on box 2.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ flex: 1, backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ flex: 3, backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
The available space is divided into one plus three, which equals four parts.
Box 1 takes 1/4 (25 percent) of the space, and box 2 takes three by four (75 percent) of the available space.
Box 2 is three times larger than box one.
boxes that don't have the flex property will continue to take up space as dictated by the Box model properties
Summary
In React Native, a View component has display Flex by default.
When you set flex 1 on a container, it takes up the entire available space.
If you set Flex on an item, it takes up a fraction or percentage of the available space.
The flex property allows for the distribution of available space among components.
=> Flex Direction
Intro
The flex Direction property establishes the main axis, which determines how the flex items are placed within the container.
By default, the main axis flows from top to bottom, causing the items to be displayed from top to bottom in the UI.
There are four different values that Flex Direction can accept.
for better visualization I will comment out boxes four to seven and keep the first three boxes only
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Value 1: Column
Column is the default value for flexDirection.
so on the flex container if I set
flexDirection: 'column',
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
we see no change in the UI.
flexDirectionwith the value ofcolumnsets the main axis from top to bottom
Value 2: column-reverse
- Sets the main axis direction from bottom to top, resulting in the flex items being placed from bottom to top.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column-reverse',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Boxes are arranged in reverse order.
Box 1 starts at the bottom of the container.
Value 3: row
- Sets the main axis to flow from left to right.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Items are placed from left to right.
Boxes seem to stretch from top to bottom, and we will talk about this in a different video
Value 4: row-reverse
- Positions the items from right to left.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row-reverse',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- Item 1 is on the right, and item 3 is on the left.
Overflow
- When all seven items are present,
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
// <View style={{ flex: 1, backgroundColor: 'plum' }}></View>
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row-reverse',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
they simply overflow.
we have the four boxes but we don't see boxes five six and seven
Summary
The Flex Direction property controls how the items are placed within the container.
The possible values are
column,column-reverse,row, androw-reverse.The Flex Direction property sets the direction of the main axis.
=> Justify Content
Intro
In this video, we will focus on the justify content property, which defines the alignment along the main axis.
let's start with the previous code
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Setting Justify Content
Justify content is a property you set on the container to control the alignment of its items.
Let's add
justifyContentto the container styles.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: '',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Default Value
By default, justify content is set to the value
flex-start.This places the flex items at the beginning of the main axis, which, in this case, is at the top of the view.
Flex Start
When justify content is set to flex-start , there is no difference in the placement of the items.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- They are still placed at the top of the view.
Flex End
- Setting justify content to
flex-endcauses the flex items to be placed at the end of the main axis.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-end',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- In this case, the items are now placed at the bottom of the view.
Center
- To align the content at the center of the main axis, set the value to
center.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- This will place the content in the middle of the view.
Additional Values
- Flexbox provides additional values that control how the extra space is distributed within the container.
Space Between
- One such value is
space-between.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'space-between',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- This evenly splits the extra space left out between the flex items.
Space Around
- For scenarios where you want space before the first item and after the last item, the value is
space-around.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'space-around',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- This adds space at the beginning and end, equal to half of the space between the flex items.
Space Evenly
- If you want the same space at the start and end of the items as well, the value you are looking for is
space-evenly.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'space-evenly',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- This equally distributes the extra space within the container.
Horizontal Alignment with Flex Direction
It's important to note that the justify content property aligns items based on the main axis.
If the flex direction is set to row (which changes the main axis from left to right),
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-start',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- justify content deals with horizontal alignment.
Summary
In summary, the justify content property is used to align items and distribute any extra space in the container.
The alignment is always along the main axis. The possible values are
flex-start,flex-end,center,space-between,space-around, andspace-evenly.
=> AlignItems
Intro
AlignItemsdefines the default behavior for laying out flex items along the container's cross axis.
Default Value of Align Items
By default, the value of AlignItems is set tostretch.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: '',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- This means the flex items will stretch the entire length of the cross axis, which typically flows from left to right.
Stretch
- Setting AlignItems to
stretchexplicitly doesn't result in any visible change since stretch is the default value.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'stretch',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- Each box stretches from the left to the right, following the direction of the cross axis.
Flex Start
- The value
flex-startpushes all the items to the start of the cross axis.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'flex-start',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Flex End
- The value
flex-endpushes the items to the end of the cross axis.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'flex-end',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Center
- The value
centercenters the content along the cross axis.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Baseline
- The
baselinevalue aligns flex items based on their content's baseline.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00', paddingVertical: 100 }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
{/* <Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'baseline',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- This value ensures that the text content sits on the same baseline, regardless of the height or padding of each flex item.
Summary
In summary, the align items property is used to align items along the cross axis.
The possible values are
flex-start,flex-end,center,baseline, andstretch(which is the default value).
=> AlignSelf
Intro
- Unlike alignItems, which is applied to the container and controls the alignment of all items within it,
alignSelfis applied to individual items, allowing us to control the alignment of each item independently.
Align Self vs Align Items
alignSelf
valuesare similar to those used in alignItems.Let's dive into VS Code to understand more.
Flex Start
The value
flex-startaligns the items at the start of the cross axis.For Box 1, let's reset alignSelf to
flex-start,
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00', alignSelf: 'flex-start' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- and you can observe the item being pulled to the left where the cross axis begins.
Flex End
The value
flex-endaligns the item with the end of the cross axis.Box 2 has align self as
flex-end,
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00', alignSelf: 'flex-start' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f', alignSelf: 'flex-end' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- resulting in the item being pulled to the right where the cross axis ends.
Center
- let's try
Centeras the third value.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00', alignSelf: 'flex-start' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f', alignSelf: 'flex-end' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- After saving the file, you can see that Box 3 is now centered.
Stretch
- The value
stretchstretches the item from the start to the end of the cross axis.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00', alignSelf: 'flex-start' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f', alignSelf: 'flex-end' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56', alignSelf: 'center' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156', alignSelf: 'stretch' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
After saving the file, you can see Box 4 gets stretched from the start to the end.
However, Boxes 5 through 7 are also stretched.
Default Value
- The default value for align self is
auto, and it inherits its value from the align items property of the parent flex container.
Inheritance from Align Items
Boxes 5 through 7 are stretched because the container has alignItems stretch as its default value.
When alignSelf is set to Auto, it takes the value of the parent's align items property, which in this case is stretch.
Hence, items 5, 6, and 7 inherit the value stretch and stretch from the left to the right in the UI.
Summary
In summary, the alignSelf property is used to align items individually and accepts values like
auto,flex-start,flex-end,center, andstretch.When specified, it always overrides the align items value of the flex container.
=> FlexWrap
Intro
- This property allows us to control how flex items behave when there is limited space within the container.
Default Value of Flex Wrap
By default, the flex-wrap property is set to
no-wrap.This means that the items will remain in a single line, even if there is not enough space.
to demonstrate this, comment out
flex: 1,and set aheight: 300,
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- and you can see when there is not enough space the items overflow the container
Using flexWrap
to prevent this overflow and handle limited space more effectively we can use the
flexWrappropertythe flexWrap property can accept one of
three possible values
No Wrap
- When the flexWrap property is set to
nowrap, there is no change in the UI, and the items remain in a single line.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
flexWrap: 'nowrap',
height: 300,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- end this is the default value
Wrap
- Setting the flex-wrap property to
wrapallows the items to wrap within the container.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
flexWrap: 'wrap',
height: 300,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- The wrapping takes place only when needed, and the items are wrapped into the next row or column.
Wrap Reverse
- The value
wrap-reverseplaces the items starting at the end of the cross axis.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
flexWrap: 'wrap-reverse',
height: 300,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- Instead of wrapping to the right column, they wrap to the previous column.
Horizontal Wrapping
Horizontal wrapping of items is possible by changing the
flex-directiontorow.With flexWrap set to
wrap, you can see the items wrapping to the next row.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
height: 300,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- Setting the value to
wrap-reverse,( uncommentflex: 1,and delete theheight),
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap-reverse',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- places the items at the end of the cross axis and wraps them to the row above.
Summary
In summary, the flex Wrap property is a valuable tool for controlling the wrapping behavior of flex items within the container.
The possible values are
nowrap(default),wrap, andwrap-reverse.
=> Align Content
Intro
In this tutorial, we will cover the Align Content property in Flexbox.
This property is used to align lines of content along the cross axis within a container.
Align Content Property Overview
The
align-contentproperty aligns lines of content along the cross axis, similar to how thealign-itemsproperty aligns individual items along the cross axis.However, a crucial condition is that multiple columns or rows must exist within the container.
Understanding Values
- To better understand the different possible values, let's make adjustments in our code using VS Code.
- Set a height on the container:
height: 300px; - Enable wrapping:
flex-wrap: wrap; - Comment out
flex: 1;to ensure wrapping into 2 columns.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
flexWrap: 'wrap',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Value 1: Flex Start
The default value for
align-contentisflex-start, which places both columns at the start of the cross axis.The cross axis runs from left to right.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
flexWrap: 'wrap',
alignContent: 'flex-start',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Value 2: Flex End
- Setting
align-contenttoflex-endpushes the content to the end of the cross axis.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
flexWrap: 'wrap',
alignContent: 'flex-end',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Value 3: Center
- Using a value of
centerforalign-contentcenters the content along the cross axis.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
flexWrap: 'wrap',
alignContent: 'center',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Value 4: Stretch
- The
stretchvalue stretches the columns from the start to the end of the cross axis.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
flexWrap: 'wrap',
alignContent: 'stretch',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- This makes both columns take up the entire horizontal space available.
Value 5: Space Between
space-betweentakes extra space and places it between the lines.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
flexWrap: 'wrap',
alignContent: 'space-between',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Fixing Styles for Space Between
You might encounter styling issues when using
space-between.To resolve this, add a fixed width and height to the box component, e.g.,
width: 50px;andheight: 50px;.
import { View, Text, StyleSheet } from 'react-native'
export default function Box({ children, style }) {
return (
<View style={[styles.box, style]}>
<Text style={styles.text}>{children}</Text>
</View>
)
}
const styles = StyleSheet.create({
box: {
backgroundColor: '#fff',
padding: 20,
width: 50,
height: 50,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
color: 'white',
},
})
Value 6: Space Around
space-arounddistributes space around the columns.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
flexWrap: 'wrap',
alignContent: 'space-around',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- The center portion has twice the space as the edges.
Summary
In React Native, the
align-contentproperty aligns lines of content along the cross axis and distributes any extra spacing in the parent container.The possible values are:
flex-start(default)flex-endcenterstretchspace-betweenspace-around
By using these values, you can control how content is aligned and distributed within a flex container.
=> gap
Intro
In this video, we will delve into the flex properties that allow us to manage spacing between rows and columns in React Native.
The three main properties we will cover are
rowGap,columnGap, andgap.
Getting Started
- Let's jump into VS Code and explore how these properties work.
Setting Up Rows and Columns:
First, ensure that we have multiple rows and columns of items within the container.
To achieve this, let's:
Comment out
flex: 1Add a
heightof 300Set
flexWraptowrap
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
flexWrap: 'wrap',
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- also in Box.js, comment out the width and height to te box.abs
import { View, Text, StyleSheet } from 'react-native'
export default function Box({ children, style }) {
return (
<View style={[styles.box, style]}>
<Text style={styles.text}>{children}</Text>
</View>
)
}
const styles = StyleSheet.create({
box: {
backgroundColor: '#fff',
padding: 20,
// width: 50,
// height: 50,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
color: 'white',
},
})
- With these styles applied, if we examine the UI, we can observe that items are organized in rows and columns.
Row Gap Property
The
rowGapproperty allows us to set the gap between rows.For instance, if we set
rowGapto 20 and save the file,
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
flexWrap: 'wrap',
rowGap: 20,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- we will notice a gap introduced between the rows.
Column Gap Property
Similarly, the
columnGapproperty sets the gap between columns.By setting it to 30 and saving the file,
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
flexWrap: 'wrap',
rowGap: 20,
columnGap: 30,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- we will see spacing between the columns.
Shorthand Gap Property
To specify the same gap for both rows and columns, you can utilize the
gapproperty.Set
gapto 10,comment out
rowGapandcolumnGap,and save the file.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
// flex: 1,
height: 300,
flexWrap: 'wrap',
// rowGap: 20,
// columnGap: 30,
gap: 10,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- This results in the same smaller gap between both rows and columns.
Summary
In summary:
To set the gap between columns, use the
columnGapproperty.To set the gap between rows, use the
rowGapproperty.For a consistent gap between both rows and columns, employ the shorthand
gapproperty.
By leveraging these properties, you can precisely control the spacing between rows and columns in your React Native layouts.
=> flexBasis
Intro
In this video, we'll delve into the flexBasis property in React Native.
The
flexBasisproperty determines the initial size of a flex item before any extra space in the container is distributed.It provides an alternative to using the height and width properties in Flex layouts.
Understanding Flex Basis
Let's gain a better understanding with a UI example.
By default, flex items have an initial height based on the Box model.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
As you can see, an item's height depends on its content size and padding in the vertical direction.
However, what if you want a specific item to be intentionally taller? For such cases, the
flexBasisproperty comes to the rescue.
Using Flex Basis
In our JSX for "Box 3", let's set flexBasis: 140
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56', flexBasis: 140 }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
Now, when we examine the UI, you can observe that "Box 3" appears twice as tall as the other boxes.
You might wonder why not just use the height property to set the initial height.
Let's try adding
height: 140to "Box 4" and look at the UI.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56', flexBasis: 140 }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156', height: 140 }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
As you can see, the result is quite similar.
However, an important difference arises when we want both "Box 3" and "Box 4" to take up the available space in the container.
We've learned to achieve this by adding
flex: 1.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56', flexBasis: 140, flex: 1 }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156', height: 140, flex: 1 }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
If we save the file and review the UI, you'll notice that "Box 3" is taller than "Box 4".
This is because the available space is distributed proportionally with respect to the flexBasis, but not the height property.
Flex Basis for Width
- It's also important to note that flexBasis sets the initial width of an item if the parent container's flex direction is row, instead of column.
In summary
the flexBasis property is a valuable tool to set the initial size of a flex item, enabling you to create intentional variations in item dimensions within your Flex layouts.
=> Flex Shrink
Intro
In this video, we'll delve into the flexShrink property in React Native.
This property determines how children within a container shrink along the main axis when their combined size exceeds the container's size.
The flex shrink factor is relative to other items within the container.
Understanding Flex Shrink
- To comprehend this concept better, let's look at an example UI.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
For simplicity, let's make a few modifications:
Comment out 5 boxes and render only two boxes
Suffix each box text with "shrink"
Change the flex direction of the container to row
Set
alignItemstoflex-startto arrange the two boxes horizontallySet the container's width to 300
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1 shrink</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2 shrink</Box>
{/* <Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'flex-start',
width: 300,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- As a result, the two items will flow horizontally but overflow the container, which has a width of 300.
Controlling Shrinking Behavior
Initially, note that every flex item has a default
flexShrinkvalue of 0.Consequently, the items overflow the container.
However, we can control the shrinking behavior by setting a positive value for
flexShrink.For example, if we apply
flexShrink: 1to the second box,
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1 shrink</Box>
<Box style={{ backgroundColor: '#b65d1f', flexShrink: 1 }}>Box 2 shrink</Box>
{/* <Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'flex-start',
width: 300,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
we'll observe that the second box shrinks to fit within the container.
By setting
flexShrinkon both boxes,
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00', flexShrink: 1 }}>Box 1 shrink</Box>
<Box style={{ backgroundColor: '#b65d1f', flexShrink: 1 }}>Box 2 shrink</Box>
{/* <Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'flex-start',
width: 300,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
they'll shrink equally to fit in.
Furthermore, the shrink factor is relative to other items.
Setting
flexShrink: 2on the second box
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00', flexShrink: 1 }}>Box 1 shrink</Box>
<Box style={{ backgroundColor: '#b65d1f', flexShrink: 2 }}>Box 2 shrink</Box>
{/* <Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box> */}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'flex-start',
width: 300,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
causes it to shrink twice as much as the first box.
This difference in shrinkage can be seen in the width of the boxes.
Summary
In summary:
The
flexShrinkproperty dictates how Flex items behave when their default size is larger than the flex container.By default, the value of
flexShrinkis 0 on every Flex item, causing items to overflow.Setting a positive value for
flexShrinkwill make items shrink when necessary, with the amount of shrinkage being relative to other items in the container.
By understanding and utilizing the flexShrink property, you can control how flex items react when there's not enough space in the container.
=> Flex Grow
Intro
In this final section, we'll explore the FlexGrow property in React Native.
flexGrowdetermines how much space an item should occupy inside a flex container when there's extra space available.Similar to
flexShrink, the flex grow factor is always relative to other items within the container.
Exploring Flex Grow
Let's gain clarity on how
flexGrowworks with an example UI.By default, flex items only occupy the space required to fit their content.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
This often leaves extra space within our container.
However, in certain situations, we want flex items to grow and utilize the remaining space.
By default, all flex items have a
flexGrowvalue of 0, preventing them from utilizing the extra space.To change this behavior, we can use the
flexGrowproperty with a positive value.
For instance:
- Initially, we'll set
flexGrowof "Item 5" to 0 (default).
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803', flexGrow: 0 }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
No change will be observed since 0 is the default value.
Changing the
flexGrowvalue to 1 for "Item 5"
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803', flexGrow: 1 }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
will cause it to grow and occupy the remaining space in the container.
Similarly, setting
flexGrowto 1 for "Item 6"
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803', flexGrow: 1 }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56', flexGrow: 1 }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
will distribute the additional space evenly between items 5 and 6.
To experiment further, changing "Item 6"'s
flexGrowvalue to 3 from 1
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803', flexGrow: 1 }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56', flexGrow: 3 }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- will cause it to grow three times more than "Item 5".
Applying Flex Grow
- Instead of setting
flexGrowindividually for each item,
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- we can add it to the common style (
box) applied to every item.
import { View, Text, StyleSheet } from 'react-native'
export default function Box({ children, style }) {
return (
<View style={[styles.box, style]}>
<Text style={styles.text}>{children}</Text>
</View>
)
}
const styles = StyleSheet.create({
box: {
backgroundColor: '#fff',
padding: 20,
flexGrow: 1,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
color: 'white',
},
})
- This will result in all items evenly utilizing the leftover space.
To summarize:
The
flexGrowproperty determines how much available space an item should take up in the flex container.The flexGrow factor is relative to other items in the container.
A default value of 0 means items shouldn't grow.
Setting
flexGrowto 1 for all flex items will cause them to grow evenly when there's extra space.
Note:
You might wonder about the relationship between the
flexproperty andflexGrow.When
flexis set to a positive number, it's equivalent to settingflexGrowwith the same number.However,
flexalso implicitly setsflexShrinkto 1 andflexBasisto 0.In essence,
flexwith a positive number is equivalent toflexGrowset to the same positive number,flexShrinkset to 1, andflexBasisset to 0.This clarifies what the
flexproperty does under the hood.
With a solid understanding of flexBasis, flexShrink, and flex-grow, you can fine-tune the behavior of flex items within your layouts.
=> Relative and Absolute Layout
Intro
In this final video of the section, we will delve into two essential layout types in React Native: relative and absolute layouts.
These layouts are based on the
positionproperty, which defines how an element is positioned within its parent container.The two possible values for the
positionproperty arerelativeandabsolute.
Understanding Relative Layout
Let's begin by looking at the default layout, which is the relative layout.
In this layout, elements are positioned according to the normal flow of the layout.
An element remains in its original position and can be offset from that position using the
top,right,bottom, andleftvalues.Importantly, this offset does not affect the positioning of any sibling or parent elements.
Exploring Absolute Layout
In contrast, the absolute layout makes an element not participate in the normal flow of the layout.
Instead, it is laid out independently of its siblings.
The position of the element is determined by the
top,right,bottom, andleftvalues, which specify specific coordinates relative to its parent container.
Visualizing with UI
- Let's take a look at these layouts using the UI in VS Code.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00' }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156' }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- We'll begin by converting each box into a square with
width: 100andheight: 100. This will create 7 square boxes in the UI.
import { View, Text, StyleSheet } from 'react-native'
export default function Box({ children, style }) {
return (
<View style={[styles.box, style]}>
<Text style={styles.text}>{children}</Text>
</View>
)
}
const styles = StyleSheet.create({
box: {
backgroundColor: '#fff',
padding: 20,
width: 100,
height: 100,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
color: 'white',
},
})
- Relative Layout:
The default layout positions elements according to the normal flow.
You can offset elements using
top,left, etc., values relative to their position.For instance, setting
top: 75andleft: 75on "Box 1" and "Box 4" will offset them.
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00', top: 75, left: 75 }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box style={{ backgroundColor: '#ab9156', top: 75, left: 75 }}>Box 4</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
- Absolute Positioning:
- Changing the
positiontoabsoluteon "Box 4" and settingtop: 100andleft: 100
import { StyleSheet, View } from 'react-native'
import Box from './components/Box'
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: '#8e9b00', top: 75, left: 75 }}>Box 1</Box>
<Box style={{ backgroundColor: '#b65d1f' }}>Box 2</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 3</Box>
<Box
style={{
backgroundColor: '#ab9156',
position: 'absolute',
top: 100,
left: 100,
}}>
Box 4
</Box>
<Box style={{ backgroundColor: '#6b0803' }}>Box 5</Box>
<Box style={{ backgroundColor: '#1c4c56' }}>Box 6</Box>
<Box style={{ backgroundColor: '#b95f21' }}>Box 7</Box>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 64,
borderWidth: 6,
borderColor: 'red',
},
})
positions it using specific coordinates relative to its parent container.
Notice that "Box 4" with absolute positioning is independent of the normal layout flow.
When to Use Each Layout
Relative Layout:
Use this when responsiveness and adaptability are crucial.
It offers a more maintainable and flexible approach for handling different screen sizes and orientations.
Absolute Layout:
Opt for this when you need precise control over position and size of UI components.
It's also useful for building custom animations with fixed coordinates.
With this, we conclude the section on layouts in React Native.
Throughout, we've learned how to use the flexbox model, understood various flexbox properties, and gained insights into the differences between relative and absolute layouts.
Section 5: Dynamic User Interfaces
=> Dynamic User Interfaces
Intro
Welcome to the fifth section of the course!
In this section, we'll delve into creating Dynamic User Interfaces in React Native.
Up until now, we've covered how to style React Native components and design layouts using flexbox.
While these aspects are vital, there's an essential piece missing.
All our learning has revolved around iPhone 14 and Pixel 4 devices.
However, it's crucial to remember that not all users of our apps will be using identical devices.
Device sizes can vary significantly, ranging from more compact phones to larger devices like iPads or Android tablets.
We must ensure that our app's interface remains responsive to these different device sizes while providing an optimal user experience.
But that's not all! Even on the same device, users might prefer different orientations—portrait or landscape.
Our UI needs to adeptly accommodate these varying user preferences.
Learning Objectives
- In this section, we will dive into several APIs that React Native provides to gracefully adapt to these changes and create dynamic user interfaces.
Setting Up
For this section on Dynamic User Interfaces, I've created a new Expo project named "DynamicUI"
In the
Appcomponent:Remove the
StatusBarimportRemove the
Textcomponent importClear all contents within the
ViewcomponentSet the
backgroundColorof the container styles to plum
import { StyleSheet, View } from 'react-native'
export default function App() {
return <View style={styles.container}></View>
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
})
Upon executing these changes, the view component displays a plum-colored background.
Alright, let's proceed to the next video where we'll learn how to work with different device sizes.
=> Dimensions API
Intro
In this video, we will explore the Dimensions API in React Native to better understand this topic.
Let's jump straight into the code with an example.
To start, we will nest a
Viewcomponent within the containerViewcomponent with thestyleprop set toStyles.box,and then a
Textcomponent withstyleset toStyles.text.For the text itself, a simple "Welcome!" is sufficient.
Let me also import the
Textcomponent which I deleted in the previous video.
import { StyleSheet, View, Text } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
})
- Now, let's define the corresponding styles we have applied to our
ViewandTextcomponents:
import { StyleSheet, View, Text } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: 300,
height: 300,
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 24,
},
})
If we save the file, the blue box and the welcome text will be displayed.
Everything looks great at this point. However, let's test the application on a larger device like an iPad.
To do this, in VSCode terminal with the terminal in focus, press
Shift + I.This will bring up a list of iOS devices.
Select "iPad Pro 6th generation." If prompted, approve the installation of the app through Expo Go.
Now the app is running on both an iPhone and an iPad, in addition to Android, of course.
However, it's clear that the app doesn't look the best on iPad.
The box is really small, and the font is not easily readable.
One potential solution to the box dimensions issue is to use percentages for width and height.
Let's set width to 70 percent and height to 40 percent.
import { StyleSheet, View, Text } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: '70%',
height: '40%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 24,
},
})
While this is an improvement, it still doesn't feel right.
The problem is that the two devices have similar height but significantly different width.
Using percentage-based width and height is not sufficient.
Moreover, how do we handle the font size? We may want a larger font, but specifying a percentage won't work.
What we need is a way to address responsive styles based on the device size.
This is where the
Dimensions APIcomes into play. Let's learn how to use it.
Step 1:
- Import the Dimensions API from React Native
import { StyleSheet, View, Text, Dimensions } from 'react-native'
Step 2:
Obtain the device width and height using the API
So right after a component:
const windowWidth = Dimensions.get('')
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const windowWidth = Dimensions.get('')
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: '70%',
height: '40%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 24,
},
})
The
getmethod accepts eitherscreenorwindowas an argument.screenrefers to the entire physical display of the device, including areas that might be outside the visible viewport due to notches, status bars, or system nav bars.On the other hand,
windowrefers to the visible area of the screen occupied by your app's UI.For tasks involving UI elements within your application's visible area, you will want to use
window.
Similarly: const windowHeight = Dimensions.get('window')
- and then we access
.widthand.height
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const windowWidth = Dimensions.get('window').width
const windowHeight = Dimensions.get('window').height
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: '70%',
height: '40%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 24,
},
})
We can now use these device dimensions to dynamically adjust the styles based on the device size.
For width, we're going to set:
width: windowWidth > 500 ? '70%' : '90%',For height:
height: windowHeight > 600 ? '60%' : '90%',For the font size:
fontSize: windowWidth > 500 ? 50 : 24,
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const windowWidth = Dimensions.get('window').width
const windowHeight = Dimensions.get('window').height
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: windowWidth > 500 ? '70%' : '90%',
height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: windowWidth > 500 ? 50 : 24,
},
})
If we save the file, you can see this is much better.
The dimensions now closely follow the device size, and the font is much easier to read on an iPad.
In summary
the Dimensions API provides access to the device size.
By using the device width or height, you can create responsive styles for your UI elements.
Now, although our styles seem to be working completely fine, the Dimensions API does have a drawback that prevents it from being the recommended approach for creating dynamic user interfaces.
Let's understand what the drawback is in the next video.
=> Dimensions API Drawback
Intro
In the previous video, we learned how to use the Dimensions API to define responsive styles for UI elements.
We successfully configured the height, width, and font size based on the device dimensions.
Although this works, we do have an issue: device dimensions can change when the screen orientation changes, and unfortunately, the Dimensions API doesn't dynamically update with these changes.
Let me help you understand with an example.
Logging Device Dimensions
- I'll log the device height and width to the console.
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const windowWidth = Dimensions.get('window').width
const windowHeight = Dimensions.get('window').height
console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: windowWidth > 500 ? '70%' : '90%',
height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: windowWidth > 500 ? 50 : 24,
},
})
When I save the file, you can see the different device dimensions are logged in the console.
The iPhone dimensions here are logged as 852 for height and 393 for width.
Now, let's rotate the iPhone device by 90 degrees using the rotate button.
Although the device switches from portrait to landscape mode, our app doesn't seem to adapt to the orientation changes as expected.
Ideally, the welcome text should also rotate similar to how one would read in portrait mode.
Adapting to Landscape Mode
Our application doesn't adapt to landscape mode because of an Expo setting defined in
app.json.By default, the orientation setting is fixed to portrait mode.
"orientation": "portrait",
As a result, our UI is tailored solely for portrait orientation.
Let's adjust this to default.
"orientation": "default",
Now when I save the file, press
Rto restart the server with the device orientation reset behind the scenes.You will see iPhone window height 852 and window width 393.
But with the orientation now set to default, if I rotate the iPhone, the app adapts and the text becomes readable once again.
However, the responsive styles don't seem to be working after rotating.
The iPhone's device width would become 852, and the device height would become 393.
This change in width and height should prompt the View component's width to change to 70 percent and the text font size to increase to 50.
Yet these changes aren't being reflected.
The width remains at 90 percent, which can be clearly inferred, and the text font size is still 24 pixels, the same as the Android device in portrait mode.
The Solution: Using Hooks and Inline Styles
The only way to recalculate the dimensions is to restart our application.When I bring focus to the device and press
Rto restart, you can see the width adjusts to 70 percent, and font size is now 50 pixels.The updated dimensions are also logged to the console.
Height is now 393, and width is 852.
But this is the drawback of the Dimensions API:
the values don't dynamically update when the window dimensions change, either due to orientation changes or more complex scenarios such as foldable phones.Of course, we do have a solution to this problem.
Let me walk you through the fix.
Step 1:
- Import
useStateanduseEffecthooks from React.
import { useState, useEffect } from 'react'
Step 2:
Create a state variable that will store the device dimensions when the screen loads.
Use the
useStatehook and pass in an object.This object will have a key called
window, which is set toDimensions.get.
import { useState, useEffect } from 'react'
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
const [dimensions, setDimensions] = useState({
window: Dimensions.get('window'),
})
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const windowWidth = Dimensions.get('window').width
const windowHeight = Dimensions.get('window').height
console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: windowWidth > 500 ? '70%' : '90%',
height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: windowWidth > 500 ? 50 : 24,
},
})
Step 3:
Add an effect that listens to changes in the device dimensions and updates the dimensions state value.
Listen to dimension changes using the static method
addEventListeneron the Dimensions API.This will return a subscription.
The
subscriptionconstant is equal toDimensions.addEventListener.
import { useState, useEffect } from 'react'
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
const [dimensions, setDimensions] = useState({
window: Dimensions.get('window'),
})
useEffect(() => {
const subscription = Dimensions.addEventListener()
})
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const windowWidth = Dimensions.get('window').width
const windowHeight = Dimensions.get('window').height
console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: windowWidth > 500 ? '70%' : '90%',
height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: windowWidth > 500 ? 50 : 24,
},
})
We listen to
changein the dimensions and execute a callback function.This function receives an object from which we destructure
windowand call thesetDimensionssetter function, passing inwindow.
import { useState, useEffect } from 'react'
import { StyleSheet, View, Text, Dimensions } from 'react-native'
return (
export default function App() {
const [dimensions, setDimensions] = useState({
window: Dimensions.get('window'),
})
useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => {
setDimensions({ window })
})
})
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const windowWidth = Dimensions.get('window').width
const windowHeight = Dimensions.get('window').height
console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: windowWidth > 500 ? '70%' : '90%',
height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: windowWidth > 500 ? 50 : 24,
},
})
- We will also clean up the subscription.
import { useState, useEffect } from 'react'
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
const [dimensions, setDimensions] = useState({
window: Dimensions.get('window'),
})
useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => {
setDimensions({ window })
})
return () => subscription?.remove()
})
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const windowWidth = Dimensions.get('window').width
const windowHeight = Dimensions.get('window').height
console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: windowWidth > 500 ? '70%' : '90%',
height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: windowWidth > 500 ? 50 : 24,
},
})
Step 4:
Extract the device width and height from the dimensions state variable.
Create constants window from dimensions,
const { window } = Dimensions,windowWidth equal to window.width,
const windowWidth = window.widthand windowHeight equal to window.height,
const windowHeight = window.height
import { useState, useEffect } from 'react'
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
const [dimensions, setDimensions] = useState({
window: Dimensions.get('window'),
})
useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => {
setDimensions({ window })
})
return () => subscription?.remove()
})
const { window } = dimensions
const windowWidth = window.width
const windowHeight = window.height
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
const windowWidth = Dimensions.get('window').width
const windowHeight = Dimensions.get('window').height
console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: windowWidth > 500 ? '70%' : '90%',
height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: windowWidth > 500 ? 50 : 24,
},
})
Step 5:
Add inline styles that dynamically change based on the device dimensions.
Comment out:
the two lines outside the component,
width and height calculation in the Stylesheet API,
and the entire text key as well
import { useState, useEffect } from 'react'
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
const [dimensions, setDimensions] = useState({
window: Dimensions.get('window'),
})
useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => {
setDimensions({ window })
})
return () => subscription?.remove()
})
const { window } = dimensions
const windowWidth = window.width
const windowHeight = window.height
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
)
}
// const windowWidth = Dimensions.get('window').width
// const windowHeight = Dimensions.get('window').height
console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
// width: windowWidth > 500 ? '70%' : '90%',
// height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
// text: {
// fontSize: windowWidth > 500 ? 50 : 24,
// },
})
- on the text component, Replace the entire
Styles.textwith an object where we setfontSize:towindowWidth > 500 ? 50 : 24.
import { useState, useEffect } from 'react'
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
const [dimensions, setDimensions] = useState({
window: Dimensions.get('window'),
})
useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => {
setDimensions({ window })
})
return () => subscription?.remove()
})
const { window } = dimensions
const windowWidth = window.width
const windowHeight = window.height
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={{ fontSize: windowWidth > 500 ? 50 : 24 }}>Welcome!</Text>
</View>
</View>
)
}
// const windowWidth = Dimensions.get('window').width
// const windowHeight = Dimensions.get('window').height
console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
// width: windowWidth > 500 ? '70%' : '90%',
// height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
// text: {
// fontSize: windowWidth > 500 ? 50 : 24,
// },
})
For the view container, use the array syntax to extend the styles,
Also, specify width and height the same as before in the Stylesheet API.
Let's also make sure to comment out the console log statement.
import { useState, useEffect } from 'react'
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
const [dimensions, setDimensions] = useState({
window: Dimensions.get('window'),
})
useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => {
setDimensions({ window })
})
return () => subscription?.remove()
})
const { window } = dimensions
const windowWidth = window.width
const windowHeight = window.height
return (
<View style={styles.container}>
<View
style={[
styles.box,
{
width: windowWidth > 500 ? '70%' : '90%',
height: windowHeight > 600 ? '60%' : '90%',
},
]}>
<Text style={{ fontSize: windowWidth > 500 ? 50 : 24 }}>Welcome!</Text>
</View>
</View>
)
}
// const windowWidth = Dimensions.get('window').width
// const windowHeight = Dimensions.get('window').height
// console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
// width: windowWidth > 500 ? '70%' : '90%',
// height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
// text: {
// fontSize: windowWidth > 500 ? 50 : 24,
// },
})
If we now save the file and go back to our iPhone device, you can see the default portrait orientation still works.
But when we change to landscape, the width and font size are adjusted to 70 and 50 pixels.
We are able to add dynamic styles to our UI elements based on device dimensions.
If you take a look at the code, though, it does seem a little verbose, doesn't it?
What if there was an easier way that abstracts all of this?
Well, there is, and that is what we will be learning in the next video.
=> useWindowDimensions
Intro
In the previous video, we discovered that the Dimensions API doesn't update when device dimensions change.
We had to implement a substantial amount of code to ensure our dynamic styles would be applied correctly when the device orientation changed.
import { useState, useEffect } from 'react'
import { StyleSheet, View, Text, Dimensions } from 'react-native'
export default function App() {
const [dimensions, setDimensions] = useState({
window: Dimensions.get('window'),
})
useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => {
setDimensions({ window })
})
return () => subscription?.remove()
})
const { window } = dimensions
const windowWidth = window.width
const windowHeight = window.height
return (
<View style={styles.container}>
<View
style={[
styles.box,
{
width: windowWidth > 500 ? '70%' : '90%',
height: windowHeight > 600 ? '60%' : '90%',
},
]}>
<Text style={{ fontSize: windowWidth > 500 ? 50 : 24 }}>Welcome!</Text>
</View>
</View>
)
}
// const windowWidth = Dimensions.get('window').width
// const windowHeight = Dimensions.get('window').height
// console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
// width: windowWidth > 500 ? '70%' : '90%',
// height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
// text: {
// fontSize: windowWidth > 500 ? 50 : 24,
// },
})
- In this video, I'll demonstrate how we can simplify this process and achieve the same outcome using the
useWindowDimensionshook.
Step 1: Eliminate Unnecessary Code
Let's start by eliminating the unnecessary code. This includes:
Removing the imports for
useStateanduseEffect.Removing the import for the Dimensions API.
Removing the entire block of code related to
useStateanduseEffect.
import { StyleSheet, View, Text } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View
style={[
styles.box,
{
width: windowWidth > 500 ? '70%' : '90%',
height: windowHeight > 600 ? '60%' : '90%',
},
]}>
<Text style={{ fontSize: windowWidth > 500 ? 50 : 24 }}>Welcome!</Text>
</View>
</View>
)
}
// const windowWidth = Dimensions.get('window').width
// const windowHeight = Dimensions.get('window').height
// console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
// width: windowWidth > 500 ? '70%' : '90%',
// height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
// text: {
// fontSize: windowWidth > 500 ? 50 : 24,
// },
})
Step 2: Import useWindowDimensions
Now, import useWindowDimensions from React Native.
import { StyleSheet, View, Text, useWindowDimensions } from 'react-native'
Step 3: Implement useWindowDimensions
- Within the component:
const windowWidth = useWindowDimensions().width
const windowHeight = useWindowDimensions().height
import { StyleSheet, View, Text, useWindowDimensions } from 'react-native'
export default function App() {
const windowWidth = useWindowDimensions().width
const windowHeight = useWindowDimensions().height
return (
<View style={styles.container}>
<View
style={[
styles.box,
{
width: windowWidth > 500 ? '70%' : '90%',
height: windowHeight > 600 ? '60%' : '90%',
},
]}>
<Text style={{ fontSize: windowWidth > 500 ? 50 : 24 }}>Welcome!</Text>
</View>
</View>
)
}
// const windowWidth = Dimensions.get('window').width
// const windowHeight = Dimensions.get('window').height
// console.log({ windowHeight, windowWidth })
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
alignItems: 'center',
justifyContent: 'center',
},
box: {
// width: windowWidth > 500 ? '70%' : '90%',
// height: windowHeight > 600 ? '60%' : '90%',
backgroundColor: 'lightblue',
alignItems: 'center',
justifyContent: 'center',
},
// text: {
// fontSize: windowWidth > 500 ? 50 : 24,
// },
})
And that's all that is required.
Save the file and return to your device.
Testing the Updated Approach
Rotate your device, and you'll notice that our dynamic styles continue to function as expected.
It is important to note that useWindowDimensions is the recommended approach.By first explaining the Dimensions API, I wanted to ensure you understand the problem it addresses.
useWindowDimensions should be a go-to approach for responsive styles.
=> SafeAreaView Component
Intro
In this video, let's learn about the Safe Area View component in React Native.
To save us some time, I'm going to copy-paste code for the
Appcomponent and walk you through the same.
Code Walkthrough
- First, we have the imports for
View,Text, andStyleSheet:
import { View, Text, StyleSheet } from 'react-native'
- Next, we have the App component as the default export:
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
// JSX code here
);
}
- For the JSX, we have an outer view container, an inner view box, and a text component. The component displays "Welcome" as the text:
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
)
}
- For the container, we have flex set to 1 to make use of the full available space and the background color of Plum:
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
},
})
- For the inner box, we have a simple padding of 20 pixels in all directions:
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
},
box: {
padding: 20,
},
})
- Finally, for the text, there is a font size of 24, font weight bold, and text align center to center-align the "Welcome" text:
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
},
box: {
padding: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
})
Issue and Solution
If you followed the series until now, this code should seem familiar.
However, when we view the app on iPhone 14 and Pixel 4 devices, we encounter a problem.
The "Welcome" text is visible on Android, but on iOS devices, it's not visible.The issue is that the text is hidden behind the notch on iOS devices.
To fix this, we can add a top padding to the container:
import { View, Text, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
paddingTop: 60,
},
box: {
padding: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
})
- But this solution is not ideal, as it introduces unnecessary space at the top for Android, and it might not work well on new devices with different notch placements.
Introducing Safe Area View
What we need is a way to figure out the safe area for a given device and render our application only within that view.
That's where the
SafeAreaViewcomponent comes in.Import it from React Native and wrap your entire JSX with it:
import { View, Text, StyleSheet, SafeAreaView } from 'react-native'
export default function App() {
return (
<SafeAreaView>
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'plum',
paddingTop: 60,
},
box: {
padding: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
})
To ensure the view occupies the entire available space, add set flex to 1
and on the component style is equal to styles dot safeContainer
import { View, Text, StyleSheet, SafeAreaView } from 'react-native'
export default function App() {
return (
<SafeAreaView style={styles.safeContainer}>
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
safeContainer: {
flex: 1,
},
container: {
flex: 1,
backgroundColor: 'plum',
paddingTop: 60,
},
box: {
padding: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
})
The purpose of SafeAreaView is to render content within the safe area boundaries of a device.
It applies padding to reflect the physical limitations of the screen, such as rounded corners or camera notches.
To avoid the white spacing at the top and bottom, apply the same background color as your container to the SafeAreaView:
let's also remove the paddingTop which we don't need anymore
import { View, Text, StyleSheet, SafeAreaView } from 'react-native'
export default function App() {
return (
<SafeAreaView style={styles.safeContainer}>
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
safeContainer: {
flex: 1,
backgroundColor: 'plum',
},
container: {
flex: 1,
backgroundColor: 'plum',
},
box: {
padding: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
})
Conclusion
Using SafeAreaView ensures that your application's content is rendered within the safe area of the device, accommodating notches and rounded corners.
This is an important aspect of adapting user interfaces based on the device. I hope you now understand the what, why, and how of SafeAreaView.
=> Platform Specific Code
Intro
When developing a cross-platform app, maximizing code reuse is a priority.
However, there are situations where it becomes necessary to tailor your code to specific platforms.
React Native offers two approaches for organizing and separating platform-specific code.
Let's learn both these approaches in this video.
Platform Module
Let's begin with the first approach: the
Platformmodule.This module is imported from React Native and detects the platform on which the app is running.
import { View, Text, StyleSheet, SafeAreaView, Platform } from 'react-native'
export default function App() {
return (
<SafeAreaView style={styles.safeContainer}>
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
safeContainer: {
flex: 1,
backgroundColor: 'plum',
},
container: {
flex: 1,
backgroundColor: 'plum',
},
box: {
padding: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
})
You can use the detection logic to implement platform-specific code.
For instance, consider our welcome text.
On iPhone, it is appropriately positioned within a safe area view, but on Pixel, it is too close to the status bar.
using the Platform module we can set a top padding only on Android:
paddingTop: Platform.OS === 'android' ? 25 : 0,
import { View, Text, StyleSheet, SafeAreaView, Platform } from 'react-native'
export default function App() {
return (
<SafeAreaView style={styles.safeContainer}>
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
safeContainer: {
flex: 1,
backgroundColor: 'plum',
},
container: {
flex: 1,
backgroundColor: 'plum',
paddingTop: Platform.OS === 'android' ? 25 : 0,
},
box: {
padding: 20,
},
text: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
},
})
This way, you can set a top padding only on Android:
you can similarly check against iOS to apply Styles specifically to iOS
paddingTop: Platform.OS === 'android' ? 25 : 0,
Platform.select
While Platform.OS is suitable for small changes, a better option for more comprehensive platform-specific styles is to use
Platform.select.Let's show an example where we completely change the text styles for iOS and Android.
We'll change the font color and font size across the two platforms.
for that within text key we're going to spread pPlatform.select and curly braces:
text: {...Platform.select({})}within this object, we can specify
ios: {}as a key to apply iOS specific Stylesand
android: {}as a key to apply Android specific styles
import { View, Text, StyleSheet, SafeAreaView, Platform } from 'react-native'
export default function App() {
return (
<SafeAreaView style={styles.safeContainer}>
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
safeContainer: {
flex: 1,
backgroundColor: 'plum',
},
container: {
flex: 1,
backgroundColor: 'plum',
paddingTop: Platform.OS === 'android' ? 25 : 0,
},
box: {
padding: 20,
},
text: {
...Platform.select({
ios: {
color: 'purple',
fontSize: 24,
fontStyle: 'italic',
},
android: {
color: 'blue',
fontSize: 30,
},
}),
fontWeight: 'bold',
textAlign: 'center',
},
})
Platform-Specific Extensions
For more complex platform-specific scenarios, you should rely on the second approach:
platform-specific extensions.In this approach, you split your code into separate files with
.iOS.jsand.Android.jsextensions before the file's main extension.React Native detects the extension and loads the relevant platform file when required by other components.
example
- Within the project folder, create a
componentsfolder and inside create aCustomButtonfolder
- Within the project folder, create a
- create two files:
CustomButton.ios.js
- create two files:
import React from 'react'
import { Pressable, Text } from 'react-native'
const CustomButton = ({ onPress, title }) => (
<Pressable
onPress={onPress}
style={{
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'lightblue',
borderRadius: 20,
padding: 10,
}}>
<Text style={{ color: 'purple', fontSize: 18 }}>{title}</Text>
</Pressable>
)
export default CustomButton
- and
CustomButton.android.js
import React from 'react'
import { Pressable, Text } from 'react-native'
const CustomButton = ({ onPress, title }) => (
<Pressable
onPress={onPress}
style={{
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'lightblue',
borderRadius: 5,
padding: 10,
}}>
<Text style={{ color: 'blue', fontSize: 18 }}>{title}</Text>
</Pressable>
)
export default CustomButton
each containing the platform-specific code for the custom button component.
- back in App.js import the folder
import { View, Text, StyleSheet, SafeAreaView, Platform } from 'react-native'
import CustomButton from './components/CustomButton/CustomButton'
export default function App() {
return (
<SafeAreaView style={styles.safeContainer}>
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome</Text>
</View>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
safeContainer: {
flex: 1,
backgroundColor: 'plum',
},
container: {
flex: 1,
backgroundColor: 'plum',
paddingTop: Platform.OS === 'android' ? 25 : 0,
},
box: {
padding: 20,
},
text: {
...Platform.select({
ios: {
color: 'purple',
fontSize: 24,
fontStyle: 'italic',
},
android: {
color: 'blue',
fontSize: 30,
},
}),
fontWeight: 'bold',
textAlign: 'center',
},
})
- and then invoke the component
By organizing your code this way, React Native will automatically select the appropriate version of the component based on the executing platform.
In summary
React Native allows you to run platform-specific code using the platform module and platform file extensions.
You can use Platform.OS or Platform.select for minor style differences.
However, for more complex components that need distinct appearances or behaviors across the two platforms, using .iOS.js and .Android.js file extensions is recommended.
With that, we conclude this section on Dynamic UI in React Native.
We've learned how to adapt the application's user interface based on device dimensions using the dimensions API, explored its drawbacks, and understood how the useWindowDimensions hook simplifies working with dimensions and responsive styles.
We've also learned about the SafeAreaView component for iOS, which ensures the application's UI adapts to hardware limitations like notches and rounded corners.
Finally, we looked at running platform-specific code, which can be quite useful as iOS and Android at times require tailor-made user experiences.
Section 6: Exercise One
=> Build a Pokemon Card 1
Intro
Welcome back! Throughout the series, we have gained a solid understanding of some of the fundamental concepts in React Native.
We have covered core components, creating custom components, styling elements, crafting layouts, and even designing responsive styles based on device dimensions.
In this sixth section of the course, we will put all this knowledge into practice by building a list of Pokemon cards in our application.
Here is what we are aiming to create:
a scrollable view displaying four Pokemon cards.
Each card has text content, an image, and styling. It is a great exercise to apply what you've learned so far.
Let's type in some code and begin.
For this section, I have set up a new project called
ExerciseOnePlease feel free to choose any project name you prefer.
To create a new React Native Expo project, use the command:
npx create-expo-app ExerciseOne
- Once you have your project set up, let's proceed to build the UI step by step.
Step 1: Modify app.js
Remove the import for StatusBar and the Text component.
Also, remove the two corresponding components from the JSX.
For the styles, keep flex as it is and change the background color to #F5F5F5.
Remove the alignItems and justifyContent properties.
With these changes in place, if we take a look at our devices,
import { StyleSheet, View } from 'react-native'
export default function App() {
return <View style={styles.container}></View>
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
})
- we will see a gray-colored View.
Step 2: Add Images
Copy and paste a few images into the assets folder.
These images will represent the four Pokemon cards.
You can find these images in my GitHub repository.
https://github.com/gopinav/React-Native-Tutorials/tree/main/ExerciseOne/assets
[Please pause the video to download them.]
Step 3: Create a Components Folder
Create a new folder called
componentswithin your project folder.Inside this folder, create a new file named
PokemonCard.jsWithin this file, define a simple React Native component.
import { View, Text } from 'react-native'
export default function PokemonCard() {
return (
<View>
<Text>Pokemon Card</Text>
</View>
)
}
Step 4: Import the Component
- Import the PokemonCard component in app.js.
import { StyleSheet, View } from 'react-native'
import PokemonCard from './components/PokemonCard'
export default function App() {
return <View style={styles.container}></View>
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
})
- Invoke this component within the JSX.
import { StyleSheet, View } from 'react-native'
import PokemonCard from './components/PokemonCard'
export default function App() {
return (
<View style={styles.container}>
<PokemonCard />
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
})
If we save the file and take a look at the devices, you should see the text "Pokemon Card" being rendered.
But the placement of the card isn't great.
Step 5: Improve Card Placement
Replace the View component import with
SafeAreaViewUpdate the JSX as well.
import { StyleSheet, SafeAreaView } from 'react-native'
import PokemonCard from './components/PokemonCard'
export default function App() {
return (
<SafeAreaView style={styles.container}>
<PokemonCard />
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
})
SafeAreaViewdoes not affect Android so let's Add top padding specific to Android devices.import
Platformmodule from react nativeand then on the container styles: padding top if platform dot operating system is equal to Android it's going to be 25 pixels else zero
(
paddingTop: Platform.OS === 'android' ? 25 : 0,)
import { StyleSheet, SafeAreaView, Platform } from 'react-native'
import PokemonCard from './components/PokemonCard'
export default function App() {
return (
<SafeAreaView style={styles.container}>
<PokemonCard />
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: Platform.OS === 'android' ? 25 : 0,
},
})
Now the component is placed in a much better position on iPhones.
On Android, the
SafeAreaViewadds padding to ensure the card is placed correctly, slightly away from the status bar.We've made significant progress in this part.
Make sure your code matches mine before moving forward. I'll see you in part two!
=> Build a Pokemon Card 2
Intro
In Part 1, we set up our container, added a background color, utilized SafeAreaView for iOS, and applied top padding for Android using the Platform module.
Now, in this video, our focus shifts towards styling our card component. Let's pick up where we left off and move on to Step 6.
Step 6: Styling with Stylesheet API
in
PokemonCard.js, Import the Stylesheet API from React Native.Use the
.create()method to define styles and assign them to thestylesconstant.
import { View, Text, StyleSheet } from 'react-native'
export default function PokemonCard() {
return (
<View>
<Text>Pokemon Card</Text>
</View>
)
}
const styles = StyleSheet.create({})
Add Card Styling
Create a new key called
cardwithin thestylesobject.Apply styling to the View component using the defined
cardstyle.
import { View, Text, StyleSheet } from 'react-native'
export default function PokemonCard() {
return (
<View style={styles.card}>
<Text>Pokemon Card</Text>
</View>
)
}
const styles = StyleSheet.create({
card: {},
})
Define Card Styles
For the card, let's start with these styles:
Background color: white,
Border radius: 16,
borderWidth: 2,
Padding: 16, (for creating spacing within the card)
Margin: 16, (for spacing between the cards)
Adding Shadow Effect
for the card Shadow effect we rely on the
PlatformmoduleImport the
Platformmodule from React Native.Utilize (spread)
...Platform.select()to apply platform-specific shadow properties.
import { View, Text, StyleSheet, Platform } from 'react-native'
export default function PokemonCard() {
return (
<View style={styles.card}>
<Text>Pokemon Card</Text>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {},
android: {},
}),
},
})
- Define shadow properties for both
iosandAndroidwithin thecardstyle.
import { View, Text, StyleSheet, Platform } from 'react-native'
export default function PokemonCard() {
return (
<View style={styles.card}>
<Text>Pokemon Card</Text>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
})
And that's it! Taking a look at our UI, the card component should now have a visually appealing appearance.
=> Build a Pokemon Card 3
Intro
Welcome to Part Three of building a Pokémon card in React Native.
In Part 2, we successfully styled our card component to resemble an actual card.
In this video, our focus will shift towards crafting the content of the card component.
Let's pick up where we left off and proceed with Step 7.
Step 7: Defining Pokémon Properties:
In
App.js, define the properties of the Pokémon that need to be rendered in the card component.For instance, let's consider Charmander:
Name: Charmander
Image: Require from
assets/charmander.pngType: Fire
HP: 39
Moves: Scratch, Ember, etc.
Weaknesses: Water and rock type Pokémon
import { StyleSheet, SafeAreaView, Platform } from 'react-native'
import PokemonCard from './components/PokemonCard'
export default function App() {
const charmanderData = {
name: 'Charmander',
image: require('./assets/charmander.png'),
type: 'Fire',
hp: 39,
moves: ['Scratch', 'Ember', 'Growl', 'Leer'],
weaknesses: ['Water', 'Rock'],
}
return (
<SafeAreaView style={styles.container}>
<PokemonCard />
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: Platform.OS === 'android' ? 25 : 0,
},
})
Step 7/2 : Passing Properties as Props:
Pass all these properties as props to the
PokemonCardcomponent using the spread operator.For instance:
import { StyleSheet, SafeAreaView, Platform } from 'react-native'
import PokemonCard from './components/PokemonCard'
export default function App() {
const charmanderData = {
name: 'Charmander',
image: require('./assets/charmander.png'),
type: 'Fire',
hp: 39,
moves: ['Scratch', 'Ember', 'Growl', 'Leer'],
weaknesses: ['Water', 'Rock'],
}
return (
<SafeAreaView style={styles.container}>
<PokemonCard {...charmanderData} />
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: Platform.OS === 'android' ? 25 : 0,
},
})
Step 8: Destructuring Props:
In
PokemonCard.js, destructure the properties from the component props.We have:
name
image
type
hp
moves
weaknesses
import { View, Text, StyleSheet, Platform } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<Text>Pokemon Card</Text>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
})
Rendering Content:
Render all these props using the appropriate core components from React Native.
A card component will have five blocks:
- Name and HP
- Pokémon image
- Pokémon type
- Moves
- Weaknesses
Step 9: Block One - Name and HP:
- Render a View with name and HP text nested inside.
import { View, Text, StyleSheet, Platform } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View>
<Text>{name}</Text>
<Text>{hp}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
})
Step 10: Block Two - Pokémon Image:
- Utilize the Image component to display the Pokémon image. Add an
accessibilityLabelfor best practice.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View>
<Text>{name}</Text>
<Text>{hp}</Text>
</View>
<Image
source={image}
accessibilityLabel={`${name} pokemon`}
/>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
})
Step 11: Block Three - Pokémon Type:
- Display the Pokémon type using the Text component.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View>
<Text>{name}</Text>
<Text>{hp}</Text>
</View>
<Image
source={image}
accessibilityLabel={`${name} pokemon`}
/>
<View>
<Text>{type}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
})
Step 12: Block Four - Moves:
- Render a list of comma-separated moves.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View>
<Text>{name}</Text>
<Text>{hp}</Text>
</View>
<Image
source={image}
accessibilityLabel={`${name} pokemon`}
/>
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
</View>
)
}
Step 13: Block Five - Weaknesses:
- Display a list of comma-separated weaknesses.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View>
<Text>{name}</Text>
<Text>{hp}</Text>
</View>
<Image
source={image}
accessibilityLabel={`${name} pokemon`}
/>
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
})
Previewing the UI:
Save the file and preview the UI.
If you find the image appearing too large, don't worry; we'll address that shortly.
For now, you can comment it out to ensure other props are rendering correctly.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View>
<Text>{name}</Text>
<Text>{hp}</Text>
</View>
{/* <Image
source={image}
accessibilityLabel={`${name} pokemon`}
/> */}
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
})
- Observe the UI, and you will now see the remaining content displayed, including name, HP, type, moves, and weaknesses.
This concludes Part 3 of our tutorial. In the next video, we will delve into styling the content within this card component.
=> Build a Pokemon Card 4
Intro
Welcome to Part 4 of building a Pokémon card in React Native.
In Part 3, we focused on the card component's content.
In this video, let's shift our concentration to styling the content.
Once again, we're picking up where we left off.
Step 14: Styling the Name and HP Block
Start with the first block that renders the name and HP.
Apply styles to each of the elements in this block.
styleprop for the wrapping view:Styles.nameContainer.styleprop for the name text element:Styles.name.styleprop for HP text element:Styles.hp.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>{hp}</Text>
</View>
{/* <Image
source={image}
accessibilityLabel={`${name} pokemon`}
/> */}
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
})
- Now Define these styles in the stylesheet object.
nameContainer Style:
Set
flexDirectiontorowto align the name and HP horizontally.Add spacing between the text items with
justifyContent: 'space-between'.Set a bottom margin for spacing with the next block.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>{hp}</Text>
</View>
{/* <Image
source={image}
accessibilityLabel={`${name} pokemon`}
/> */}
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 32,
},
})
Name Text Style:
Increase font size.
Set font weight to bold.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>{hp}</Text>
</View>
{/* <Image
source={image}
accessibilityLabel={`${name} pokemon`}
/> */}
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
})
HP Text Style:
Set font size to 22.
Add a heart emoji in the jsx to represent HP.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>❤️{hp}</Text>
</View>
{/* <Image
source={image}
accessibilityLabel={`${name} pokemon`}
/> */}
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
hp: {
fontSize: 22,
},
})
Step 15: Styling the Image Block
- For the second block (image), add
styleprop:Styles.image.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>❤️{hp}</Text>
</View>
<Image
style={styles.image}
source={image}
accessibilityLabel={`${name} pokemon`}
/>
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
hp: {
fontSize: 22,
},
})
Define this style in the stylesheet object.
Set width to 100%.
Set height to 200.
Add margin bottom for spacing.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>❤️{hp}</Text>
</View>
<Image
style={styles.image}
source={image}
accessibilityLabel={`${name} pokemon`}
/>
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
hp: {
fontSize: 22,
},
image: {
width: '100%',
height: 200,
marginBottom: 16,
},
})
- To fix cropping, add
resizeModeprop to the image component and set it tocontain.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>❤️{hp}</Text>
</View>
<Image
style={styles.image}
source={image}
accessibilityLabel={`${name} pokemon`}
resizeMode='contain'
/>
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
hp: {
fontSize: 22,
},
image: {
width: '100%',
height: 200,
marginBottom: 16,
},
})
- Now the image block is perfectly positioned.
Conclusion:
I'll stop here as we're halfway through the styling process. Let's continue with the remaining styles in Part 5.
=> Build a Pokemon Card 5
Intro
Welcome to Part 5 of building a Pokémon card in React Native.
In Part 4, we concentrated on styling the first two blocks within our card component.
In this video, let's shift our attention to the remaining three blocks.
Once again, we will pick up where we left off.
Step 16: Styling the Pokémon type Block
Apply styles to the third block that displays the Pokémon
type.We'll render the type in a badge along with an emoji representing the Pokémon type.
Include a function that returns badge styles based on the Pokémon type.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
const getTypeDetails = type => {
switch (type.toLowerCase()) {
case 'electric':
return { borderColor: '#FFD700', emoji: '⚡️' }
case 'water':
return { borderColor: '#6493EA', emoji: '💧' }
case 'fire':
return { borderColor: '#FF5733', emoji: '🔥' }
case 'grass':
return { borderColor: '#66CC66', emoji: '🌿' }
default:
return { borderColor: '#A0A0A0', emoji: '❓' }
}
}
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>❤️{hp}</Text>
</View>
<Image
style={styles.image}
source={image}
accessibilityLabel={`${name} pokemon`}
resizeMode='contain'
/>
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
hp: {
fontSize: 22,
},
image: {
width: '100%',
height: 200,
marginBottom: 16,
},
})
this function
getTypeDetailsreturns a border color and an emoji based on thePokemon type
Invoke this function to get
borderColorandemojibased on the type.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
const getTypeDetails = type => {
switch (type.toLowerCase()) {
case 'electric':
return { borderColor: '#FFD700', emoji: '⚡️' }
case 'water':
return { borderColor: '#6493EA', emoji: '💧' }
case 'fire':
return { borderColor: '#FF5733', emoji: '🔥' }
case 'grass':
return { borderColor: '#66CC66', emoji: '🌿' }
default:
return { borderColor: '#A0A0A0', emoji: '❓' }
}
}
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
const { borderColor, emoji } = getTypeDetails(type)
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>❤️{hp}</Text>
</View>
<Image
style={styles.image}
source={image}
accessibilityLabel={`${name} pokemon`}
resizeMode='contain'
/>
<View>
<Text>{type}</Text>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
hp: {
fontSize: 22,
},
image: {
width: '100%',
height: 200,
marginBottom: 16,
},
})
Apply styles to the type block:
Wrap the text element with a View representing the badge.
Create another View to Display the corresponding emoji within the badge view by
duplicating the line and binding
{emoji}, which we have destructured fromgetTypeDetailsfunction
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
const getTypeDetails = type => {
switch (type.toLowerCase()) {
case 'electric':
return { borderColor: '#FFD700', emoji: '⚡️' }
case 'water':
return { borderColor: '#6493EA', emoji: '💧' }
case 'fire':
return { borderColor: '#FF5733', emoji: '🔥' }
case 'grass':
return { borderColor: '#66CC66', emoji: '🌿' }
default:
return { borderColor: '#A0A0A0', emoji: '❓' }
}
}
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
const { borderColor, emoji } = getTypeDetails(type)
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>❤️{hp}</Text>
</View>
<Image
style={styles.image}
source={image}
accessibilityLabel={`${name} pokemon`}
resizeMode='contain'
/>
<View>
<View>
<Text>{emoji}</Text>
<Text>{type}</Text>
</View>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
hp: {
fontSize: 22,
},
image: {
width: '100%',
height: 200,
marginBottom: 16,
},
})
Apply styles to each element:
styles.typeContainer,styles.badge,styles.typeEmoji, andstyles.typeText.so for the outer container style is equal to
styles.typeContainerfor the inner view component style is equal to an array
[], where we applystyles.badge, which we are going to Define, and we also saidborderColor`` to the Border color destructured fromgetTypeDetails`next on the Emoji text style is equal to
styles.typeEmoji
-and then style is equal to styles.typeText
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
const getTypeDetails = type => {
switch (type.toLowerCase()) {
case 'electric':
return { borderColor: '#FFD700', emoji: '⚡️' }
case 'water':
return { borderColor: '#6493EA', emoji: '💧' }
case 'fire':
return { borderColor: '#FF5733', emoji: '🔥' }
case 'grass':
return { borderColor: '#66CC66', emoji: '🌿' }
default:
return { borderColor: '#A0A0A0', emoji: '❓' }
}
}
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
const { borderColor, emoji } = getTypeDetails(type)
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>❤️{hp}</Text>
</View>
<Image
style={styles.image}
source={image}
accessibilityLabel={`${name} pokemon`}
resizeMode='contain'
/>
<View style={styles.typeContainer}>
<View style={[styles.badge, { borderColor }]}>
<Text style={styles.typeEmoji}>{emoji}</Text>
<Text style={styles.typeText}>{type}</Text>
</View>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
hp: {
fontSize: 22,
},
image: {
width: '100%',
height: 200,
marginBottom: 16,
},
})
Define the key value pairs for each of these styles
typeContainer:
badge:
typeEmoji:
typeText:
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
const getTypeDetails = type => {
switch (type.toLowerCase()) {
case 'electric':
return { borderColor: '#FFD700', emoji: '⚡️' }
case 'water':
return { borderColor: '#6493EA', emoji: '💧' }
case 'fire':
return { borderColor: '#FF5733', emoji: '🔥' }
case 'grass':
return { borderColor: '#66CC66', emoji: '🌿' }
default:
return { borderColor: '#A0A0A0', emoji: '❓' }
}
}
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
const { borderColor, emoji } = getTypeDetails(type)
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>❤️{hp}</Text>
</View>
<Image
style={styles.image}
source={image}
accessibilityLabel={`${name} pokemon`}
resizeMode='contain'
/>
<View style={styles.typeContainer}>
<View style={[styles.badge, { borderColor }]}>
<Text style={styles.typeEmoji}>{emoji}</Text>
<Text style={styles.typeText}>{type}</Text>
</View>
</View>
<View>
<Text>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '#333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
hp: {
fontSize: 22,
},
image: {
width: '100%',
height: 200,
marginBottom: 16,
},
typeContainer: {
alignItems: 'center',
marginBottom: 40,
},
badge: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 6,
paddingHorizontal: 12,
borderRadius: 20,
borderWidth: 4,
},
typeEmoji: {
fontSize: 30,
marginRight: 12,
},
typeText: {
fontSize: 22,
fontWeight: 'bold',
},
})
Step 17: Styling the Moves Block
Display the list of moves in the JSX.
Apply styles to the moves jsx block:
Styles.movesContainerandStyles.movesText.And let's define the key value pairs for the styles
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
const getTypeDetails = type => {
switch (type.toLowerCase()) {
case 'electric':
return { borderColor: '#FFD700', emoji: '⚡️' }
case 'water':
return { borderColor: '#6493EA', emoji: '💧' }
case 'fire':
return { borderColor: '#FF5733', emoji: '🔥' }
case 'grass':
return { borderColor: '#66CC66', emoji: '🌿' }
default:
return { borderColor: '#A0A0A0', emoji: '❓' }
}
}
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
const { borderColor, emoji } = getTypeDetails(type)
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>❤️{hp}</Text>
</View>
<Image
style={styles.image}
source={image}
accessibilityLabel={`${name} pokemon`}
resizeMode='contain'
/>
<View style={styles.typeContainer}>
<View style={[styles.badge, { borderColor }]}>
<Text style={styles.typeEmoji}>{emoji}</Text>
<Text style={styles.typeText}>{type}</Text>
</View>
</View>
<View style={styles.movesContainer}>
<Text style={styles.movesText}>Moves: {moves.join(', ')}</Text>
</View>
<View>
<Text>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '#333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
hp: {
fontSize: 22,
},
image: {
width: '100%',
height: 200,
marginBottom: 16,
},
typeContainer: {
alignItems: 'center',
marginBottom: 40,
},
badge: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 6,
paddingHorizontal: 12,
borderRadius: 20,
borderWidth: 4,
},
typeEmoji: {
fontSize: 30,
marginRight: 12,
},
typeText: {
fontSize: 22,
fontWeight: 'bold',
},
movesContainer: {
marginBottom: 16,
},
movesText: {
fontSize: 22,
fontWeight: 'bold',
},
})
Step 18: Styling the Weakness Block
Style the weakness block similar to the moves block.
Apply styles to the weakness block:
Styles.weaknessContainerandstyles.weaknessText.
import { View, Text, StyleSheet, Platform, Image } from 'react-native'
const getTypeDetails = type => {
switch (type.toLowerCase()) {
case 'electric':
return { borderColor: '#FFD700', emoji: '⚡️' }
case 'water':
return { borderColor: '#6493EA', emoji: '💧' }
case 'fire':
return { borderColor: '#FF5733', emoji: '🔥' }
case 'grass':
return { borderColor: '#66CC66', emoji: '🌿' }
default:
return { borderColor: '#A0A0A0', emoji: '❓' }
}
}
export default function PokemonCard({ name, image, type, hp, moves, weaknesses }) {
const { borderColor, emoji } = getTypeDetails(type)
return (
<View style={styles.card}>
<View style={styles.nameContainer}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.hp}>❤️{hp}</Text>
</View>
<Image
style={styles.image}
source={image}
accessibilityLabel={`${name} pokemon`}
resizeMode='contain'
/>
<View style={styles.typeContainer}>
<View style={[styles.badge, { borderColor }]}>
<Text style={styles.typeEmoji}>{emoji}</Text>
<Text style={styles.typeText}>{type}</Text>
</View>
</View>
<View style={styles.movesContainer}>
<Text style={styles.movesText}>Moves: {moves.join(', ')}</Text>
</View>
<View style={styles.weaknessContainer}>
<Text style={styles.weaknessText}>Weakness: {weaknesses.join(', ')}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
card: {
backgroundColor: 'white',
borderRadius: 16,
borderWidth: 2,
padding: 16,
margin: 16,
...Platform.select({
ios: {
shadowOffset: { width: 2, height: 2 },
shadowColor: '#333',
shadowOpacity: 0.3,
shadowRadius: 4,
},
android: {
elevation: 5,
},
}),
},
nameContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
marginBottom: 32,
},
name: {
fontSize: 30,
fontWeight: 'bold',
},
hp: {
fontSize: 22,
},
image: {
width: '100%',
height: 200,
marginBottom: 16,
},
typeContainer: {
alignItems: 'center',
marginBottom: 40,
},
badge: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 6,
paddingHorizontal: 12,
borderRadius: 20,
borderWidth: 4,
},
typeEmoji: {
fontSize: 30,
marginRight: 12,
},
typeText: {
fontSize: 22,
fontWeight: 'bold',
},
movesContainer: {
marginBottom: 16,
},
movesText: {
fontSize: 22,
fontWeight: 'bold',
},
weaknessContainer: {
marginBottom: 8,
},
weaknessText: {
fontSize: 22,
fontWeight: 'bold',
},
})
Conclusion:
With this, we have successfully completed styling our Pokémon card component. In the sixth and final part, we will add multiple Pokémon cards to our view.
=> Build a Pokemon Card 6
Intro
Welcome to the final part of building a Pokémon card in React Native.
In this video, let's render multiple Pokémon cards.
Once again, we will pick up where we left off.
Step 19: Defining Pokémon Details
- In App.js, Define the Pokémon details for three other Pokémon (Squirtle, Bulbasaur, and Pikachu).
import { StyleSheet, SafeAreaView, Platform } from 'react-native'
import PokemonCard from './components/PokemonCard'
export default function App() {
const charmanderData = {
name: 'Charmander',
image: require('./assets/charmander.png'),
type: 'Fire',
hp: 39,
moves: ['Scratch', 'Ember', 'Growl', 'Leer'],
weaknesses: ['Water', 'Rock'],
}
const squirtleData = {
name: 'Squirtle',
image: require('./assets/squirtle.png'), // Replace with the actual image path
type: 'Water',
hp: 44,
moves: ['Tackle', 'Water Gun', 'Tail Whip', 'Withdraw'],
weaknesses: ['Electric', 'Grass'],
}
const bulbasaurData = {
name: 'Bulbasaur',
image: require('./assets/bulbasaur.png'), // Replace with the actual image path
type: 'Grass',
hp: 45,
moves: ['Tackle', 'Vine Whip', 'Growl', 'Leech Seed'],
weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
}
const pikachuData = {
name: 'Pikachu',
image: require('./assets/pikachu.png'), // Replace with the actual image path
type: 'Electric',
hp: 35,
moves: ['Quick Attack', 'Thunderbolt', 'Tail Whip', 'Growl'],
weaknesses: ['Ground'],
}
return (
<SafeAreaView style={styles.container}>
<PokemonCard {...charmanderData} />
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: Platform.OS === 'android' ? 25 : 0,
},
})
- Invoke the
PokemonCardcomponent three more times and pass the newly defined objects as props.
<PokemonCard {...SquirtleData} />
<PokemonCard {...BulbasaurData} />
<PokemonCard {...PikachuData} />
Step 20: Adding a ScrollView
Import the
ScrollViewcomponent from React Native.Add the
ScrollViewcomponent to make the view scrollable.
import { StyleSheet, SafeAreaView, Platform, ScrollView } from 'react-native'
import PokemonCard from './components/PokemonCard'
export default function App() {
const charmanderData = {
name: 'Charmander',
image: require('./assets/charmander.png'),
type: 'Fire',
hp: 39,
moves: ['Scratch', 'Ember', 'Growl', 'Leer'],
weaknesses: ['Water', 'Rock'],
}
const squirtleData = {
name: 'Squirtle',
image: require('./assets/squirtle.png'), // Replace with the actual image path
type: 'Water',
hp: 44,
moves: ['Tackle', 'Water Gun', 'Tail Whip', 'Withdraw'],
weaknesses: ['Electric', 'Grass'],
}
const bulbasaurData = {
name: 'Bulbasaur',
image: require('./assets/bulbasaur.png'), // Replace with the actual image path
type: 'Grass',
hp: 45,
moves: ['Tackle', 'Vine Whip', 'Growl', 'Leech Seed'],
weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
}
const pikachuData = {
name: 'Pikachu',
image: require('./assets/pikachu.png'), // Replace with the actual image path
type: 'Electric',
hp: 35,
moves: ['Quick Attack', 'Thunderbolt', 'Tail Whip', 'Growl'],
weaknesses: ['Ground'],
}
return (
<SafeAreaView style={styles.container}>
<ScrollView>
<PokemonCard {...charmanderData} />
<PokemonCard {...squirtleData} />
<PokemonCard {...bulbasaurData} />
<PokemonCard {...pikachuData} />
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: Platform.OS === 'android' ? 25 : 0,
},
})
- Ensure that all the cards are visible and scrollable in the UI.
Final Checks:
Test the app with different device dimensions and orientations to ensure it functions properly.
In the terminal, press Shift + I and select different device profiles to verify the UI's responsiveness.
Edit
app.jsonto set the orientation to "default" for better adaptability.Restart the application and rotate the device to ensure all four cards render as expected without issues.
Conclusion:
I hope this exercise has helped you apply all the concepts you've learned so far in the series.
With this, we have completed building the Pokémon card application in React Native.
Section 7: How to render list
=> Lists
Intro
Rendering lists is an essential aspect of mobile application development.
Whether it's a list of contacts, products, or any other collection of items, lists are a fundamental UI component.
In this seventh section of the course, we will explore how to render lists in React Native.
Setting Up the Project:
For this section on lists, I have created a new Expo project called
RNListYou can create a similar project using the command:
npx create-expo-app RNList
Step 1: Creating Mock Data
We'll start by creating a mock data set that represents a list of Pokémon.
Each Pokémon will have properties like ID, type, and name.
Create a file called
data.jsonwithin the project folder.Copy and paste a list of 100 Pokémon into
data.json.
Step 2: Importing the Data
- Import the Pokémon list from
data.jsonin your JavaScript file.
import { StatusBar } from 'expo-status-bar'
import { StyleSheet, Text, View } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<StatusBar style='auto' />
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
})
Step 3: Rendering the List
To render the list, we can use the
mapmethod.Within the
Viewcontainer, use curly braces andmapover thePokemonListarray.
mport { StatusBar } from 'expo-status-bar'
import { StyleSheet, Text, View } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return <View style={styles.container}>{pokemonList.map()}</View>
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
})
For each Pokémon, return a
Viewcomponent that renders the Pokémon type and name.Remember to specify a unique
keyprop for each item, in this case, usePokemon.id.
import { StatusBar } from 'expo-status-bar'
import { StyleSheet, Text, View } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<View style={styles.container}>
{pokemonList.map(pokemon => {
return (
<View key={pokemon.id}>
<Text>{pokemon.typeList[0]}</Text>
<Text>{pokemon.name}</Text>
</View>
)
})}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
})
Step 4: Making the List Scrollable
By default, the
Viewcomponent is not scrollable.To fix this, import the
ScrollViewcomponent from React Native.Wrap your list with the
ScrollViewcomponent to make it scrollable.
import { StatusBar } from 'expo-status-bar'
import { StyleSheet, Text, View, ScrollView } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<View style={styles.container}>
<ScrollView>
{pokemonList.map(pokemon => {
return (
<View key={pokemon.id}>
<Text>{pokemon.typeList[0]}</Text>
<Text>{pokemon.name}</Text>
</View>
)
})}
</ScrollView>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
})
Step 5: Styling Improvements
1: Add Safe Area View
Import
SafeAreaViewand replace the outermostViewcontainer withSafeAreaView.This adds top padding on iOS devices.
import { StatusBar } from 'expo-status-bar'
import { StyleSheet, Text, View, ScrollView, SafeAreaView } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
<ScrollView>
{pokemonList.map(pokemon => {
return (
<View key={pokemon.id}>
<Text>{pokemon.typeList[0]}</Text>
<Text>{pokemon.name}</Text>
</View>
)
})}
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
})
- To accommodate Android devices, import
StatusBar(from react native instead of Expo) and setpaddingToptoStatusBar.currentHeight.
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
<ScrollView>
{pokemonList.map(pokemon => {
return (
<View key={pokemon.id}>
<Text>{pokemon.typeList[0]}</Text>
<Text>{pokemon.name}</Text>
</View>
)
})}
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
paddingTop: StatusBar.currentHeight,
},
})
Add some styles
let's also change the background color to
#F5F5F5and remove
alignItemsandjustifyContent
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
<ScrollView>
{pokemonList.map(pokemon => {
return (
<View key={pokemon.id}>
<Text>{pokemon.typeList[0]}</Text>
<Text>{pokemon.name}</Text>
</View>
)
})}
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
})
2: Add Styling to the ScrollView
Create a style for the
ScrollViewby adding ascrollViewkey to theStylesobject and set a horizontal padding to 16.Apply this style to the
ScrollViewcomponent.
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
<ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View key={pokemon.id}>
<Text>{pokemon.typeList[0]}</Text>
<Text>{pokemon.name}</Text>
</View>
)
})}
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
})
3: Create a card Style
Create a style for each list item (card).
Set a white background color, padding, border radius, border width, and margin bottom.
Apply this style to the
Viewcomponent for each Pokémon.
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
<ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text>{pokemon.typeList[0]}</Text>
<Text>{pokemon.name}</Text>
</View>
)
})}
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 16,
},
})
4: Create a Text Style
Create a style for the text content inside the card. so
cardText:Increase the font size for better readability.
Apply this style to both text components.
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
<ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.typeList[0]}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
Conclusion:
While our current code works fine, rendering lists using the ScrollView component and the map method is not the preferred technique. In the next video, we will explore why this is the case and learn about the recommended approach.
=> FlatList
Intro
In the previous video We learned how to render a list in React Native using the
ScrollViewcomponent and themapmethod.However, with this method, all items are rendered even if they're not in the viewport.
While rendering all list items might not be an issue for small lists, for larger data sets consisting of thousands of items, it can lead to performance problems.
The recommended approach
- In React Native to rendering lists is to use the
FlatListcomponent, which renders only the items currently in view, making it highly performant for long lists.
Let's see it in action:
- Comment out the
ScrollViewcomponent code. - Import the
FlatListcomponent from React Native. - Replace the map method code with the
FlatListcomponent code.
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<FlatList />
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
- The Flat List is a self-closing component and has two mandatory props:
- data={}: The
dataprop is used to pass the array of items that you want to render in the list.
- In our case, it is the
pokemonlist
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<FlatList data={pokemonList} />
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
- renderItem={}: The
renderItemprop is a function that returns the JSX for rendering each item in the list.
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<FlatList
data={pokemonList}
renderItem={() => {}}
/>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
The function receives an object containing the current
item, and you can define how each item should be rendered.This is the same as what we return from the
mapmethod.So, copy the return statement and paste it.
We do have to change "pokemon" to "item".
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<FlatList
data={pokemonList}
renderItem={({ item }) => {
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
/>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
if we now take a look at the UI. We can still see our list, and we can scroll down to the bottom, but this list is more performant.
Let's verify that by logging the item ID to the console:
Within the render item function, console.log(item.ID).
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
/>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
take a look at the terminal. We can see the first 64 IDs instead of 100.
Now you might think that this is incorrect. We see six or seven items in the viewport but we see 64 in the terminal. How is that right?
Well, the thing is,
the FlatList component ensures it generates a few devices length of content in advance to ensure smooth scrollingBut now that the initial number of cards have been rendered, if we scroll down a bit, you can see the additional IDs being logged in the console.
They are lazily loaded to ensure smooth scrolling.If you were to try this experiment with an array of 50 Pokemon, you would see all 50 in the logs and would assume FlatList has no performance benefits.
I wanted to make sure you don't walk away with that wrong idea, and hence the long list of items.
horizontal prop
On a side note, if you want to render the list horizontally, you can add the horizontal={} prop and set it to true
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
horizontal={true}
/>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
- You can see the cards are now rendered horizontally.
keyExtractor prop
If this is clear, let me highlight one more prop before we proceed to the next video:
The prop is
keyExtractor={}This prop is a function that takes an item from the data array and returns a unique key for that item.
It is used to identify the unique items in the list, similar to the key prop when using the map method.
By default, it returns
keyExtractor={item.key}and if that is not present, it returns
keyExtractor={item.id}If that as well is not present, it uses the item
indexas key.Index, by the way, is available as the second argument to the callback function, where the first argument is the
itemitself.keyExtractor={(item, index) => }In our case, we do have an
Idproperty for every item, and the same is used by default.However, you can define it yourself. So
itemreturnsitem.Idand we convert this to a string, as the function should return a string, (but I believe there is implicit conversion for a numeric type).
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
// horizontal={true}
/>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
- If we save the file, our UI should still be working as expected.
Adding horizontal padding
Now we are missing the horizontal padding, so you can wrap the FlatList component with a View component and add style equal to styles.scrollView.
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
<FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
// horizontal={true}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
- If we save the file, a list of Pokemon should look much better.
Apart from data, renderItem, horizontal, and keyExtractor, there are quite a few props that can be used to customize the behavior and appearance of the Flat List. Let's see what they are in the upcoming videos.
to summarize
But to summarize what we have learned in this video:
A
ScrollViewrenders all its React child components at once and has a performance downside.FlatList, on the other hand, renders items easily when they're about to appear and removes items that scroll way off the screen to save memory and processing time.Using
FlatListis a recommended approach to render lists in React Native.
=> ItemSeparator
Intro
In the previous video, we learned about the FlatList component in React Native.
We explored essential props like
data,renderItem, andkeyExtractor.Now, let's delve into some additional props commonly used with the FlatList component.
ItemSeparatorComponent
In this video, we will focus on the
ItemSeparatorComponentThis prop accepts a React component, rendering it between each item in the list but excluding the top and the bottom.
In our case, it's the perfect prop to replace the bottom margin we have on the card component.
While these 16 pixels serve as a useful separator between the cards, it also introduces unnecessary spacing at the bottom of the list.
By specifying a
Viewcomponent with a16-pixel height as the item separator component and commenting out the margin-bottom property,
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
<FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
// horizontal={true}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
you'll notice that the spacing between the cards is still maintained, but the extra space at the end of the list is eliminated.
Generally, spacing is the most elegant way to separate list items, but you are free to customize the separator component to align with your design preferences.
In summary
the
item separator componentinserts a component between each item in the list, excluding the top and bottom.You can assign any React component or even a function that returns a React component.
The function can receive specific props, allowing for more intricate styling of the component, but I will leave that for you to explore further when you have the time.
=> listEmpty
Intro
The next prop we will explore is the
listEmptyComponentThis prop accepts a React component and renders it when the list is empty.
Let's specify a simple
Textcomponent:"No items found."If we now specify the
dataprop as an empty array ([]) and take a look at the UI,
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
<FlatList
data={[]}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={<Text>No items found</Text>}
// horizontal={true}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
we can see the text
"No items found."The text is the component assigned to the
list empty componentprop.We have already covered styling and layout, so I won't dive into making this component look pretty.
I will leave that to you as a practice exercise.
Try increasing the font size and centering this text in the UI.
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
<FlatList
data={[]}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={
<Text style={{ fontSize: 35, textAlign: 'center' }}>No items found</Text>
}
}
// horizontal={true}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
flex: 1,
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
Let me know how it goes in the comment section.
As you can see, the
list empty componentprop is straightforward and incredibly useful, especially when you fetch data from an API, and the data might be empty.Instead of handling the logic yourself, you can use this prop to manage empty states efficiently.
=> List Header and Footer
Intro
two more props supported by FlatList component the
ListHeaderComponentand theListFooterComponentthey allow you to add custom header and footer components to your lists
ListHeaderComponent
This prop allows you to add a custom header component to the top of the list.
It accepts a React component and renders it at the top of the list.
Perfect for adding titles, descriptions, or any other content you want to display above your list items.Example: Add a simple text component that says "Pokémon List" with the style set to
styles.header_text.
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
<FlatList
data={[]}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={
<View style={{ justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 35 }}>No items found</Text>
</View>
}
ListHeaderComponent={<Text style={styles.headerText}>Pokemon List</Text>}
// horizontal={true}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
flex: 1,
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
})
In the Styles section, define the styles with a font size of 24, text-align center, and a bottom margin of 12.
When you change the header to "Pokémon List" and view the UI,
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
<FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={
<Text style={{ fontSize: 35, textAlign: 'center' }}>No items found</Text>
}
ListHeaderComponent={<Text style={styles.headerText}>Pokemon List</Text>}
// horizontal={true}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
flex: 1,
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
headerText: {
fontSize: 24,
textAlign: 'center',
marginBottom: 12,
},
})
- you can see the text "Pokémon List" at the top.
List Footer Component
This prop allows you to add a custom footer component to the bottom of the list.
It accepts a React component and renders it at the bottom of the list.Useful for adding footnotes, pagination controls, or indicating the end of the list.
Example: Add a text component that says "End of List" with the style set to
styles.footer_text.- In the Styles section, define the styles with a font size of 24, text-align center, and a margin-top of 12.
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
<FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={
<Text style={{ fontSize: 35, textAlign: 'center' }}>No items found</Text>
}
ListHeaderComponent={<Text style={styles.headerText}>Pokemon List</Text>}
ListFooterComponent={<Text style={styles.footerText}>End if list</Text>}
// horizontal={true}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
flex: 1,
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
headerText: {
fontSize: 24,
textAlign: 'center',
marginBottom: 12,
},
footerText: {
fontSize: 24,
textAlign: 'center',
marginTop: 12,
},
})
- When you scroll all the way to the bottom of the list, you can see the text "End of List."
Whether you're displaying a catalog of products, a collection of articles, or a list of Pokémon, both these props can help you create more engaging and user-friendly interfaces.
SectionList
Intro
In this final video of the section on lists in React Native, we will explore the
SectionListcomponentAs the name suggests, it is a performant component designed for rendering sectioned lists.
We will render a list of Pokémon categorized by
type, with sections for different types.Begin by creating a mock data set representing a list of Pokemon by type
within the project folder I will create a file
grouped-data.Jsonin this file I'll paste Json data which you can find on my GitHub repo
[
{
"type": "Grass",
"data": ["Bulbasaur", "Ivysaur", "Venusaur"]
},
{
"type": "Fire",
"data": ["Charmander", "Charmeleon", "Charizard"]
},
{
"type": "Water",
"data": ["Squirtle", "Wartortle", "Blastoise"]
},
{ "type": "Electric", "data": ["Pikachu", "Raichu"] }
]
it consists of an array of four objects
and each object contains two key value pairs
typerepresenting the Pokemon type anddatarepresenting the list of Pokemon for that typeit is crucial to name this property
dataas that is what theSectionListcomponent expectsif your API data set for example doesn't have the
dataproperty you would have to transform your datathe other property though can be named anything and I've chosen to call it
typeas it seems logical
Step 1
Comment out the FlatList component from before.
We still need the wrapping View component for horizontal padding.
import { StyleSheet, Text, View, ScrollView, SafeAreaView, StatusBar, FlatList } from 'react-native'
import pokemonList from './data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
{/* <FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={
<Text style={{ fontSize: 35, textAlign: 'center' }}>No items found</Text>
}
ListHeaderComponent={<Text style={styles.headerText}>Pokemon List</Text>}
ListFooterComponent={<Text style={styles.footerText}>End if list</Text>}
// horizontal={true}
/> */}
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
flex: 1,
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
headerText: {
fontSize: 24,
textAlign: 'center',
marginBottom: 12,
},
footerText: {
fontSize: 24,
textAlign: 'center',
marginTop: 12,
},
})
Step 2
Import the
SectionListcomponent from React Native.Import the JSON data from
grouped-data.json.
import {
StyleSheet,
Text,
View,
ScrollView,
SafeAreaView,
StatusBar,
FlatList,
SectionList,
} from 'react-native'
import pokemonList from './data.json'
import groupedPokemonList from './grouped-data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
{/* <FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={
<Text style={{ fontSize: 35, textAlign: 'center' }}>No items found</Text>
}
ListHeaderComponent={<Text style={styles.headerText}>Pokemon List</Text>}
ListFooterComponent={<Text style={styles.footerText}>End if list</Text>}
// horizontal={true}
/> */}
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
flex: 1,
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
headerText: {
fontSize: 24,
textAlign: 'center',
marginBottom: 12,
},
footerText: {
fontSize: 24,
textAlign: 'center',
marginTop: 12,
},
})
Step 3
Invoke the
SectionListcomponent with the required props.The Section List component has two mandatory props:
- sections:
It is the source of information for the Section List.
In our case, it is
groupedPokemonList.
- renderItem:
This prop is a function that returns the JSX for rendering each item in the list.
It receives an object containing the current
item, and you can define how each item should be rendered.This is similar to what we return for the
FlatListcomponent but without the Pokémon type.
import {
StyleSheet,
Text,
View,
ScrollView,
SafeAreaView,
StatusBar,
FlatList,
SectionList,
} from 'react-native'
import pokemonList from './data.json'
import groupedPokemonList from './grouped-data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
{/* <FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={
<Text style={{ fontSize: 35, textAlign: 'center' }}>No items found</Text>
}
ListHeaderComponent={<Text style={styles.headerText}>Pokemon List</Text>}
ListFooterComponent={<Text style={styles.footerText}>End if list</Text>}
// horizontal={true}
/> */}
<SectionList
sections={groupedPokemonList}
renderItem={({ item }) => {
return (
<View style={styles.card}>
<Text style={styles.cardText}>{item}</Text>
</View>
)
}}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
flex: 1,
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
headerText: {
fontSize: 24,
textAlign: 'center',
marginBottom: 12,
},
footerText: {
fontSize: 24,
textAlign: 'center',
marginTop: 12,
},
})
Grouping by Pokémon Type
To group the list by Pokémon type, add a prop called renderSectionHeader
This prop is a function that returns the JSX for rendering each section header in the list.
It receives an object containing the current
section, and you can define how each section header should be rendered.We render
section.typeand add some simple styling.
import {
StyleSheet,
Text,
View,
ScrollView,
SafeAreaView,
StatusBar,
FlatList,
SectionList,
} from 'react-native'
import pokemonList from './data.json'
import groupedPokemonList from './grouped-data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
{/* <FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={
<Text style={{ fontSize: 35, textAlign: 'center' }}>No items found</Text>
}
ListHeaderComponent={<Text style={styles.headerText}>Pokemon List</Text>}
ListFooterComponent={<Text style={styles.footerText}>End if list</Text>}
// horizontal={true}
/> */}
<SectionList
sections={groupedPokemonList}
renderItem={({ item }) => {
return (
<View style={styles.card}>
<Text style={styles.cardText}>{item}</Text>
</View>
)
}}
renderSectionHeader={({ section }) => {
return <Text style={styles.sectionHeaderText}>{section.type}</Text>
}}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
flex: 1,
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
headerText: {
fontSize: 24,
textAlign: 'center',
marginBottom: 12,
},
footerText: {
fontSize: 24,
textAlign: 'center',
marginTop: 12,
},
})
Styling Section Headers
Define styles for section headers.
Styles include a white background color, font size 24, and font weight bold.
import {
StyleSheet,
Text,
View,
ScrollView,
SafeAreaView,
StatusBar,
FlatList,
SectionList,
} from 'react-native'
import pokemonList from './data.json'
import groupedPokemonList from './grouped-data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
{/* <FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={
<Text style={{ fontSize: 35, textAlign: 'center' }}>No items found</Text>
}
ListHeaderComponent={<Text style={styles.headerText}>Pokemon List</Text>}
ListFooterComponent={<Text style={styles.footerText}>End if list</Text>}
// horizontal={true}
/> */}
<SectionList
sections={groupedPokemonList}
renderItem={({ item }) => {
return (
<View style={styles.card}>
<Text style={styles.cardText}>{item}</Text>
</View>
)
}}
renderSectionHeader={({ section }) => {
return <Text style={styles.sectionHeaderText}>{section.type}</Text>
}}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
flex: 1,
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
headerText: {
fontSize: 24,
textAlign: 'center',
marginBottom: 12,
},
footerText: {
fontSize: 24,
textAlign: 'center',
marginTop: 12,
},
sectionHeaderText: {
backgroundColor: 'white',
fontSize: 24,
fontWeight: 'bold',
},
})
ItemSeparatorComponent
- The
SectionListcomponent accepts many of the same props asFlatList, includingItemSeparatorComponent
import {
StyleSheet,
Text,
View,
ScrollView,
SafeAreaView,
StatusBar,
FlatList,
SectionList,
} from 'react-native'
import pokemonList from './data.json'
import groupedPokemonList from './grouped-data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
{/* <FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={
<Text style={{ fontSize: 35, textAlign: 'center' }}>No items found</Text>
}
ListHeaderComponent={<Text style={styles.headerText}>Pokemon List</Text>}
ListFooterComponent={<Text style={styles.footerText}>End if list</Text>}
// horizontal={true}
/> */}
<SectionList
sections={groupedPokemonList}
renderItem={({ item }) => {
return (
<View style={styles.card}>
<Text style={styles.cardText}>{item}</Text>
</View>
)
}}
renderSectionHeader={({ section }) => {
return <Text style={styles.sectionHeaderText}>{section.type}</Text>
}}
ItemSeparatorComponent={() => <View style={{ height: 16 }} />}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
flex: 1,
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
headerText: {
fontSize: 24,
textAlign: 'center',
marginBottom: 12,
},
footerText: {
fontSize: 24,
textAlign: 'center',
marginTop: 12,
},
sectionHeaderText: {
backgroundColor: 'white',
fontSize: 24,
fontWeight: 'bold',
},
})
SectionSeparatorComponent
- and
SectionSeparatorComponents
import {
StyleSheet,
Text,
View,
ScrollView,
SafeAreaView,
StatusBar,
FlatList,
SectionList,
} from 'react-native'
import pokemonList from './data.json'
import groupedPokemonList from './grouped-data.json'
export default function App() {
return (
<SafeAreaView style={styles.container}>
{/* <ScrollView style={styles.scrollView}>
{pokemonList.map(pokemon => {
return (
<View
style={styles.card}
key={pokemon.id}>
<Text style={styles.cardText}>{pokemon.type}</Text>
<Text style={styles.cardText}>{pokemon.name}</Text>
</View>
)
})}
</ScrollView> */}
<View style={styles.scrollView}>
{/* <FlatList
data={pokemonList}
renderItem={({ item }) => {
console.log(item.id)
return (
<View
style={styles.card}
key={item.id}>
<Text style={styles.cardText}>{item.type}</Text>
<Text style={styles.cardText}>{item.name}</Text>
</View>
)
}}
keyExtractor={item => item.id.toString()}
ItemSeparatorComponent={<View style={{ height: 16 }} />}
ListEmptyComponent={
<Text style={{ fontSize: 35, textAlign: 'center' }}>No items found</Text>
}
ListHeaderComponent={<Text style={styles.headerText}>Pokemon List</Text>}
ListFooterComponent={<Text style={styles.footerText}>End if list</Text>}
// horizontal={true}
/> */}
<SectionList
sections={groupedPokemonList}
renderItem={({ item }) => {
return (
<View style={styles.card}>
<Text style={styles.cardText}>{item}</Text>
</View>
)
}}
renderSectionHeader={({ section }) => {
return <Text style={styles.sectionHeaderText}>{section.type}</Text>
}}
ItemSeparatorComponent={() => <View style={{ height: 16 }} />}
SectionSeparatorComponent={() => <View style={{ height: 16 }} />}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#f5f5f5',
paddingTop: StatusBar.currentHeight,
},
scrollView: {
paddingHorizontal: 16,
},
card: {
flex: 1,
backgroundColor: 'white',
padding: 16,
borderRadius: 8,
borderWidth: 1,
// marginBottom: 16,
},
cardText: {
fontSize: 30,
},
headerText: {
fontSize: 24,
textAlign: 'center',
marginBottom: 12,
},
footerText: {
fontSize: 24,
textAlign: 'center',
marginTop: 12,
},
sectionHeaderText: {
backgroundColor: 'white',
fontSize: 24,
fontWeight: 'bold',
},
})
- Be sure to return (
() =>) the View component instead of directly assigning it to the ItemSeparator or SectionSeparator component.
Conclusion
With the SectionListComponent, we have learned how to render grouped data efficiently in React Native.
This concludes the section on lists in React Native, where we explored various list rendering methods, including the Map method, Scroll View component, FlatList component, and SectionListComponent.
Section 8: Inputs and Forms
=> Inputs and Forms
Intro
Welcome back to the eighth section of our course, where we will focus on inputs and forms in React Native.
In this introductory video, we will clarify some key aspects and set expectations for what we are about to learn.
Input Options in React Native
In web applications, we have numerous HTML elements for capturing user input, such as input fields, text areas, drop-down menus, checkboxes, radio buttons, and more.
However, in React Native, our input options are more limited. As of this recording, the core React Native library provides only two input components:
TextInputandSwitchThese will be our primary focus in this section.
Expo Expands Input Options
- Expo, an additional toolkit for React Native, offers more input components like checkboxes and date pickers as part of the Expo SDK. We will explore these in a separate section.
Challenges in React Native Forms
- Working with forms in React Native presents challenges. We typically focus on four key aspects:
- Managing form state.
- Handling form validation.
- Displaying validation messages.
- Submitting form data.
- While libraries like React Hook Form can streamline these steps, we will learn how to handle forms manually without external dependencies in this section.
React Hook Form
- In the future, we might dedicate an entire section to combining React Native with React Hook Form, so stay tuned for that.
Project Setup
To follow along, I've created a new Expo project named
RNFormsYou can create a similar project using the command:
npx create-expo-app RNForms
=> TextInput
TextInput
Intro
The Text Input component is a fundamental building block for user input in React Native.
It allows users to enter text and other data into your application.
Getting Started
I'm going to start by making some changes in App.js
Remove the
expo-status-barimport from Expo and instead importStatusBarfromreact-nativeto stick with vanilla RNImport
SafeAreaViewand replace theViewcomponent withSafeAreaViewto handle iOS padding at the top.For Android, add padding equal to the status bar height in the container styles.
paddingTop: StatusBar.currentHeightRemove any alignment properties on the container that may affect text input placement.
Delete the JSX within the
SafeAreaView.
import { StyleSheet, Text, View, StatusBar, SafeAreaView } from 'react-native'
export default function App() {
return <SafeAreaView style={styles.container}></SafeAreaView>
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
})
Adding Text Input
We are now ready for our text input.
Begin by importing it from React Native and invoke the component within
SafeAreaView.
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
export default function App() {
return (
<SafeAreaView style={styles.container}>
<TextInput />
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
})
However, when you view it on your devices, you may not see the text input component.
The styling is such that you don't really see it.
If you try to click on the input, you can see the cursor.
Styling the Text Input
Let's add some styles to fix this.
Let's call the key
inputand add a height of 40 pixels, margin, padding, and border width.Assign this style to the
TextInputcomponent
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
export default function App() {
return (
<SafeAreaView style={styles.container}>
<TextInput style={styles.input} />
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
})
Now, when you view the UI, you can clearly see the text input component.
You can focus on the input and start typing.
On Android, it brings up the keyboard, and you can continue typing.
For iOS, if you want a similar keyboard, you need to press
Command+Shift+K.This will bring up the keyboard, and you can press different keys.
Press
Command+Shift+Kagain, and the keyboard disappears.
Tracking Input Value with useState
However, the value you've entered isn't being tracked.
To track the input value, we can make use of a
statevariable similar to how we do it in React.Import
useStatefrom Reactand create a state variable
nameand the correspondingsetNamefunction with an initial value of an empty string.
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput style={styles.input} />
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
})
- Assign
nameto thevalueprop on theTextInputand assignsetNamefunction to theonChangeTextprop of the text input component.
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
/>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
})
- This will automatically update the state variable when you start typing in the input box.
Displaying Input Value
- Let's add some text to make sure we are tracking the input value:
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
/>
<Text>My name is {name}</Text>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
})
- Let's also add some styles to make it clear:
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
/>
<Text style={styles.text}>My name is {name}</Text>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
})
- Now, when you start typing on the devices, you can see the same value is reflected in the text component below.
Summary
The
TextInputcomponent is a tool for inputting text into the app via a keyboard.You can import it from the React Native library and invoke it with the
valueandonChangeTextprops to manage its state.Join me in the next video where we will learn about some of the important props supported by the text input component.
=> TextInput Props
TextInput Props
Intro
In the previous video, we explored the fundamental aspects of the TextInput component in React Native.
In this video, let's dive into some important props that allow you to customize the behavior and appearance of the TextInput component.
Placeholder Prop
First, we have the
placeholderprop.It provides users with visual cues about the expected input.
For example, we can specify
placeholder= 'email@example.com'
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder='email@example.com'
/>
<Text style={styles.text}>My name is {name}</Text>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
})
When we take a look at the UI, you can see that before the user starts typing, the placeholder text is displayed.
This guides them on what information to provide, in our case, an email.
Please remember that
a placeholder is not a substitute for a label, as the hint disappears once you start typing.
SecureTextEntry Prop
The second prop is
secureTextEntry.Enabling this prop masks the input characters, making them appear as asterisks or dots.
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder='email@example.com'
secureTextEntry
/>
<Text style={styles.text}>My name is {name}</Text>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
})
This ensures that the user's input remains hidden when dealing with sensitive information like passwords.
The
secureTextEntryprop is crucial for privacy and security.
keyboardType Prop
The third prop to be aware of is
keyboardType.This prop allows you to specify the type of keyboard that appears when the user interacts with the text input.
For example, if you have created a phone number field, you can set
keyboardType = 'numeric'
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder='email@example.com'
secureTextEntry
keyboardType='numeric'
/>
<Text style={styles.text}>My name is {name}</Text>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
})
Returning to the UI and focusing on the text input field on Android, you can see the numeric keyboard.
There are a few other values you can specify, but you can see how this prop enhances the user experience.
AutoCorrection and AutoCapitalization
Let me walk you through two more props which are sometimes not favored but are enabled by default.
If I restart the app and head back to the UI, if I were to type "watre" and press Enter, you can see it auto-corrects to "water."
Also, if I type "john doe" and press Enter, you can see it auto-capitalizes the last name.
While this behavior is valid for some scenarios, for a username or email field that doesn't always follow perfect English, you might want to relax these enforcements.
To do that, you can set
autoCorrect={false}autoCapitalize="none"
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder='email@example.com'
// secureTextEntry
// keyboardType='numeric'
autoCorrect={false}
autoCapitalize='none'
/>
<Text style={styles.text}>My name is {name}</Text>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
})
- Other values for
autoCapitalizeinclude capitalizing everycharacter, the first character of everywords, or the first character of everysentences(which is the default).
=> Multiline TextInput
Multiline TextInput
Intro
In this video, we will explore how to define and style a multiline input in React Native.
Unlike the web, which has an input element for single-line input and a text area element for multiline input, in React Native, the same text input element can be used for both purposes.
All we have to do is include the
multilineprop.
Creating a Multi-line Input
Let's add a new
TextInputcomponent.We're going to set
styleas equal toStyles.input,placeholderas equal to "message," and then add themultilineprop
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder='email@example.com'
// secureTextEntry
// keyboardType='numeric'
autoCorrect={false}
autoCapitalize='none'
/>
<TextInput
style={styles.input}
placeholder='message'
multiline
/>
<Text style={styles.text}>My name is {name}</Text>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
})
- If we take a look at the UI, we don't see much difference since the underlying element is the same.
Styling the multiline Input
To make this multiline input stand out, we need to add some additional styles.
In the
Stylesobject, add a new key-value pairmultilineText: { minHeight: 100 }Now, we need to specify an array of styles for the TextInput
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder='email@example.com'
// secureTextEntry
// keyboardType='numeric'
autoCorrect={false}
autoCapitalize='none'
/>
<TextInput
style={[styles.input, styles.multilineText]}
placeholder='message'
multiline
/>
<Text style={styles.text}>My name is {name}</Text>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
multilineText: {
minHeight: 100,
},
})
If we take a look at the UI, it is clear that this now resembles a multi-line text input.
A user will be able to understand from the get-go that they can input longer text, which is always good.
Addressing Alignment
Now, it's important to note that the
multilineprop aligns the text input to the top on iOS and centers it on Android.To address this, we need to specify
textAlignVerticaland set it to "top."
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder='email@example.com'
// secureTextEntry
// keyboardType='numeric'
autoCorrect={false}
autoCapitalize='none'
/>
<TextInput
style={[styles.input, styles.multilineText]}
placeholder='message'
multiline
/>
<Text style={styles.text}>My name is {name}</Text>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
multilineText: {
minHeight: 100,
textAlignVertical: 'top',
},
})
- If we head back to the UI, we can see it is now properly aligned to the top.
Summary
- That covers the text input component in React Native, including how to create a multi-line input and style it to meet your design requirements.
=> Switch
Intro
In this video, let's explore the
Switchcomponent in React Native.The
Switchcomponent serves as a valuable tool for integrating toggles into your app's user interface.It is particularly well suited for scenarios where you require users to make binary choices, such as enabling or disabling specific app features.
Usage Steps
- Let's head back to VS Code and understand its usage step by step:
Step 1: Importing the Switch Component
- Begin by importing the Switch component from the React Native library.
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput, Switch } from 'react-native'
Step 2: Creating a Container
Let's use the
Viewcomponent from React Native and create a container for the switch component.We're going to set
stylesas equal tostyles.switchContainer.Inside this container, nest a
Textcomponent that says "Dark mode" with a style set tostyles.text. Then, invoke the switch component.
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput, Switch } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder='email@example.com'
// secureTextEntry
// keyboardType='numeric'
autoCorrect={false}
autoCapitalize='none'
/>
<TextInput
style={[styles.input, styles.multilineText]}
placeholder='message'
multiline
/>
<Text style={styles.text}>My name is {name}</Text>
<View style={styles.switchContainer}>
<Text style={styles.text}>Dark Mode</Text>
<Switch />
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
multilineText: {
minHeight: 100,
textAlignVertical: 'top',
},
})
Step 3: Defining Styles
Let's define the styles for the switchContainer.
Set
flexDirectionto "row,"alignItemsto "center,"justifyContentto "space-between," and finally,paddingHorizontalto 10.
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput, Switch } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder='email@example.com'
// secureTextEntry
// keyboardType='numeric'
autoCorrect={false}
autoCapitalize='none'
/>
<TextInput
style={[styles.input, styles.multilineText]}
placeholder='message'
multiline
/>
<Text style={styles.text}>My name is {name}</Text>
<View style={styles.switchContainer}>
<Text style={styles.text}>Dark Mode</Text>
<Switch />
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
multilineText: {
minHeight: 100,
textAlignVertical: 'top',
},
switchContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 10,
},
})
- Now, we will have 10 pixels of horizontal padding for the text "dark mode," but that is okay as styling isn't really the main focus.
Step 4: Connecting with state variable
For step 4, create a new state variable named
isDarkModewith an initial value offalse.Then, add two props to the switch component:
valueis equal toisDarkMode,and
onValueChange, where we toggle theisDarkModevalue.We pass in a function that receives the
previousStateand set it to the inverse of the previous state,!previousStatebasically inverting the value.
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput, Switch } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
const [isDarkMode, setIsDarkMode] = useState(false)
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder='email@example.com'
// secureTextEntry
// keyboardType='numeric'
autoCorrect={false}
autoCapitalize='none'
/>
<TextInput
style={[styles.input, styles.multilineText]}
placeholder='message'
multiline
/>
<Text style={styles.text}>My name is {name}</Text>
<View style={styles.switchContainer}>
<Text style={styles.text}>Dark Mode</Text>
<Switch
value={isDarkMode}
onValueChange={() => setIsDarkMode(previousState => !previousStates)}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
multilineText: {
minHeight: 100,
textAlignVertical: 'top',
},
switchContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 10,
},
})
- In a practical scenario, you can consider showing different content or activating certain features in your application when the switch is turned on or off.
Customizing Appearance using trackColor and thumbColor
As for the props, we can use
trackColorto set colors for the track in different states.This is an object. If the value is
false, let's set it to#767577, and if it istrue, we set it to light blue.We can also set the thumb color using the
thumbColorprop, let's set it to#F4F3F4.
import { StyleSheet, Text, View, StatusBar, SafeAreaView, TextInput, Switch } from 'react-native'
import { useState } from 'react'
export default function App() {
const [name, setName] = useState('')
const [isDarkMode, setIsDarkMode] = useState(false)
return (
<SafeAreaView style={styles.container}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder='email@example.com'
// secureTextEntry
// keyboardType='numeric'
autoCorrect={false}
autoCapitalize='none'
/>
<TextInput
style={[styles.input, styles.multilineText]}
placeholder='message'
multiline
/>
<Text style={styles.text}>My name is {name}</Text>
<View style={styles.switchContainer}>
<Text style={styles.text}>Dark Mode</Text>
<Switch
value={isDarkMode}
onValueChange={() => setIsDarkMode(previousState => !previousState)}
trackColor={{ false: '#767577', true: 'lightblue' }}
thumbColor={'#f4f3f4'}
/>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: StatusBar.currentHeight,
},
input: {
height: 40,
margin: 12,
padding: 10,
borderWidth: 1,
},
text: {
fontSize: 30,
padding: 10,
},
multilineText: {
minHeight: 100,
textAlignVertical: 'top',
},
switchContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 10,
},
})
If we take a look at the UI, we can see we have different colors from before.
Ideally, these would be dictated by your brand colors.
Platform Differences
- You can also see that the appearance of the switch component is different across the two platforms.
=> Login Form
Intro
Welcome back! In our previous videos, we have covered inputs in React Native.
Now, let's talk about forms.
We're going to be learning this through a series of four videos where we will be building a login form from scratch.
In this particular video, our focus will be on building the user interface for our login form.
This means we'll be writing the JSX and styling it.
Setting Up the Component
I have reset the app component to minimal code.
We have imports for
View,Text,TextInput,Button, andStyleSheet. The JSX is empty.
import { View, Text, TextInput, Button, StyleSheet } from 'react-native'
export default function App() {
return ()
}
const styles = StyleSheet.create({})
- Now, let's add the elements for the login form.
Creating the Main Container
First things first, we'll begin with a
Viewcomponent.Think of this as our main container, and of course, we're going to apply some container styles to make it look neat.
styleis equal tostyles.container, and we're going to define the container styles:flexset to1,justifyContentset tocenter, a bit of padding in the horizontal direction, and a slightly gray background color.
import { View, Text, TextInput, Button, StyleSheet } from 'react-native'
export default function App() {
return <View style={styles.container}></View>
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
})
Form Container
Next, inside this container, let's nest another
Viewcomponent.This will act as our form container, and you guessed it, we're going to style it using form styles.
styleis set tostyles.form, and we're going to specify a white background color, padding, border radius, shadow color, shadow offset, shadow opacity, shadow radius, and elevation. This is specifically for Android.
import { View, Text, TextInput, Button, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.form}></View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
})
Adding Labels and Inputs
Now it's time to add our labels and inputs. We'll need one for username and one for the password.
For our inputs, let's add a placeholder to guide our users: "Enter your username" and "Enter your password."
For the password input, always use
secureTextEntryto keep passwords hidden.
import { View, Text, TextInput, Button, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.form}>
<Text>Username</Text>
<TextInput placeholder='Enter your username' />
<Text>Password</Text>
<TextInput
placeholder='Enter your password'
secureTextEntry
/>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
})
- Let's style these components: For the label,
styleis equal tostyles.label, and for the text input,styleis equal tostyles.input.
import { View, Text, TextInput, Button, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.form}>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
/>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
})
For the label, we set font size, margin bottom, font weight,
and for the input, we define height, border color, border width, margin bottom, padding, and border radius.
import { View, Text, TextInput, Button, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.form}>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
/>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
})
Adding a Submit Button
Moving on, we will add a button that users can click to submit this form data.
We will title it "Login," and as for the
onPressprop, we will leave it empty for now. We'll tackle that in an upcoming video.
import { View, Text, TextInput, Button, StyleSheet } from 'react-native'
export default function App() {
return (
<View style={styles.container}>
<View style={styles.form}>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
/>
<Button
title='Login'
onPress={() => {}}
/>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
})
Managing Form State
If we save the file and take a look at the UI, we can see our login form with username, password, and the login button.
For our final step, we're going to manage the form state using the
useStatehook.Let's import it at the top.
import { useState } from 'react'
We'll then create two state variables:
username(empty string as the default value)and
password(empty string as the default value).
import { View, Text, TextInput, Button, StyleSheet } from 'react-native'
import { useState } from 'react'
export default function App() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
return (
<View style={styles.container}>
<View style={styles.form}>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
/>
<Button
title='Login'
onPress={() => {}}
/>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
})
On the text inputs, we add
valueis equal tousernameandonChangeTextis equal tosetUsername.Similarly,
valueis equal topassword, andonChangeTextis equal tosetPassword.
import { View, Text, TextInput, Button, StyleSheet } from 'react-native'
import { useState } from 'react'
export default function App() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
return (
<View style={styles.container}>
<View style={styles.form}>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
value={username}
onChangeText={setUsername}
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
value={password}
onChangeText={setPassword}
/>
<Button
title='Login'
onPress={() => {}}
/>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
})
- We can now manage our form state seamlessly.
=> KeyboardAvoidingView
Intro
In our last video, we built the UI for our login form, but as with most things in development, there is a common hiccup we run into when dealing with forms in React Native.
Let me show that to you.
Adding an Image
First, let's make a small change in our UI.
Above the username label, I'm going to render an image.
Make sure to import the component from React Native.
We're going to require the Adaptive icon from the assets folder:
We will also add
styleis equal toStyles.image,and with the StyleSheet API, a new key
imagewith a height of 200, width of 200,alignSelfset tocenter, andmarginBottomof 50.
import { View, Text, TextInput, Button, StyleSheet, Image } from 'react-native'
import { useState } from 'react'
export default function App() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
return (
<View style={styles.container}>
<View style={styles.form}>
<Image
style={styles.image}
source={require('./assets/adaptive-icon.png')}
/>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
value={username}
onChangeText={setUsername}
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
value={password}
onChangeText={setPassword}
/>
<Button
title='Login'
onPress={() => {}}
/>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
image: {
width: 200,
height: 200,
alignSelf: 'center',
marginBottom: 50,
},
})
The Keyboard Overlay Issue
Now, if we jump back to our UI, we can see the form with the Adaptive icon image.
If we tap on the password field on the iOS device and press
Command+Shift+Kto bring up the keyboard, do you notice something off?The keyboard completely overlays the input element; it's practically invisible.
And let's be honest, this is not a great user experience.
Using KeyboardAvoidingView
Luckily, React Native has a solution for us: the
KeyboardAvoidingViewcomponent.Let's import it from React Native.
Now, replace our outermost
Viewcontainer withKeyboardAvoidingView, both opening and closing tags.On this component, we also set a prop
behavioris equal topadding.This ensures that the component increases its padding at the bottom, matching the keyboard's height.
import {
View,
Text,
TextInput,
Button,
StyleSheet,
Image,
KeyboardAvoidingView,
} from 'react-native'
import { useState } from 'react'
export default function App() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
return (
<KeyboardAvoidingView
behavior='padding'
style={styles.container}>
<View style={styles.form}>
<Image
style={styles.image}
source={require('./assets/adaptive-icon.png')}
/>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
value={username}
onChangeText={setUsername}
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
value={password}
onChangeText={setPassword}
/>
<Button
title='Login'
onPress={() => {}}
/>
</View>
</KeyboardAvoidingView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
image: {
width: 200,
height: 200,
alignSelf: 'center',
marginBottom: 50,
},
})
Addressing the Image Issue
Let's take a look at our UI, tap on the password, and our form gracefully moves up, revealing the input element.
Problem solved! Well, not entirely.
I'm going to set the image height to 400.
image: {
width: 200,
height: 400,
alignSelf: 'center',
marginBottom: 50,
},
Now, if we revisit our UI, you'll notice that the input is hiding again when the keyboard pops up.
Once again, we have a fix, and that is a prop called
keyboardVerticalOffset.Setting it to 100 usually does the trick.
import {
View,
Text,
TextInput,
Button,
StyleSheet,
Image,
KeyboardAvoidingView,
} from 'react-native'
import { useState } from 'react'
export default function App() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
return (
<KeyboardAvoidingView
behavior='padding'
keyboardVerticalOffset={100}
style={styles.container}>
<View style={styles.form}>
<Image
style={styles.image}
source={require('./assets/adaptive-icon.png')}
/>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
value={username}
onChangeText={setUsername}
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
value={password}
onChangeText={setPassword}
/>
<Button
title='Login'
onPress={() => {}}
/>
</View>
</KeyboardAvoidingView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
image: {
width: 200,
height: 400,
alignSelf: 'center',
marginBottom: 50,
},
})
Handling Platform-Specific Offset
But wait, there is a tiny problem for our Android users. When I tap on the password field, you can see there is this unnecessary space that is introduced by the offset.
So what is the solution? Well, a platform-specific offset.
Let's import the
Platformmodule and adjust our offset to only apply to iOS devices:
import {
View,
Text,
TextInput,
Button,
StyleSheet,
Image,
KeyboardAvoidingView,
Platform,
} from 'react-native'
import { useState } from 'react'
export default function App() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
return (
<KeyboardAvoidingView
behavior='padding'
keyboardVerticalOffset={Platform.OS === 'ios' ? 100 : 0}
style={styles.container}>
<View style={styles.form}>
<Image
style={styles.image}
source={require('./assets/adaptive-icon.png')}
/>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
value={username}
onChangeText={setUsername}
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
value={password}
onChangeText={setPassword}
/>
<Button
title='Login'
onPress={() => {}}
/>
</View>
</KeyboardAvoidingView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
image: {
width: 200,
height: 400,
alignSelf: 'center',
marginBottom: 50,
},
})
- If we restart our application and take a look at our UI, we see that the UI is much better on both Android and iOS.
Conclusion
The
KeyboardAvoidingViewcan be a tricky component in React Native; it might test your patience.But with the basics we have covered today, you're well positioned to deal with it.
=> Form Validation
Intro
In this video, Let's dive into form validation.
We will make sure our email and password fields have mandatory field validation.
to display error messages
We need a way to display error messages to our users.
Let's start by creating a new state variable to store these messages.
We're going to call this
errors, the functionsetErrors, and its initial value is an empty object.
import {
View,
Text,
TextInput,
Button,
StyleSheet,
Image,
KeyboardAvoidingView,
Platform,
} from 'react-native'
import { useState } from 'react'
export default function App() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [errors, setErrors] = useState({})
return (
<KeyboardAvoidingView
behavior='padding'
keyboardVerticalOffset={Platform.OS === 'ios' ? 100 : 0}
style={styles.container}>
<View style={styles.form}>
<Image
style={styles.image}
source={require('./assets/adaptive-icon.png')}
/>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
value={username}
onChangeText={setUsername}
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
value={password}
onChangeText={setPassword}
/>
<Button
title='Login'
onPress={() => {}}
/>
</View>
</KeyboardAvoidingView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
image: {
width: 200,
height: 400,
alignSelf: 'center',
marginBottom: 50,
},
})
- This object will help us keep track of any errors that might pop up.
Define a Validation Function
Now, let's define a function called
validateForm.This function will be the heart of our validation logic.
It will check the fields and either populate our
errorsobject with messages or return true if everything is valid.Similarly, one for the password.
import {
View,
Text,
TextInput,
Button,
StyleSheet,
Image,
KeyboardAvoidingView,
Platform,
} from 'react-native'
import { useState } from 'react'
export default function App() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [errors, setErrors] = useState({})
const validateForm = () => {
let errors = {}
if (!username) errors.username = 'Username is require'
if (!password) errors.password = 'Password is require'
}
return (
<KeyboardAvoidingView
behavior='padding'
keyboardVerticalOffset={Platform.OS === 'ios' ? 100 : 0}
style={styles.container}>
<View style={styles.form}>
<Image
style={styles.image}
source={require('./assets/adaptive-icon.png')}
/>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
value={username}
onChangeText={setUsername}
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
value={password}
onChangeText={setPassword}
/>
<Button
title='Login'
onPress={() => {}}
/>
</View>
</KeyboardAvoidingView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
image: {
width: 200,
height: 400,
alignSelf: 'center',
marginBottom: 50,
},
})
- We will then call
setErrorspassing in theerrorsobject.
import {
View,
Text,
TextInput,
Button,
StyleSheet,
Image,
KeyboardAvoidingView,
Platform,
} from 'react-native'
import { useState } from 'react'
export default function App() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [errors, setErrors] = useState({})
const validateForm = () => {
let errors = {}
if (!username) errors.username = 'Username is require'
if (!password) errors.password = 'Password is require'
setErrors(errors)
return Object.keys(errors).length === 0
}
return (
<KeyboardAvoidingView
behavior='padding'
keyboardVerticalOffset={Platform.OS === 'ios' ? 100 : 0}
style={styles.container}>
<View style={styles.form}>
<Image
style={styles.image}
source={require('./assets/adaptive-icon.png')}
/>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
value={username}
onChangeText={setUsername}
/>
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
value={password}
onChangeText={setPassword}
/>
<Button
title='Login'
onPress={() => {}}
/>
</View>
</KeyboardAvoidingView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
image: {
width: 200,
height: 400,
alignSelf: 'center',
marginBottom: 50,
},
})
- So, if the
errorsobject doesn't have a key for username or password, we don't have any error messages, and our form is valid.
Displaying Error Messages
But if you do have errors, we need to display them.
In our JSX, let's add error messages using a text component right after the text input.
We're going to check if a username error is present and render that message. If there is no error, we return null.
We'll also style this using
Styles.errorText, which we will define shortly.I'm going to copy this, paste it after the other text input, and change occurrences of "username" to "password".
import {
View,
Text,
TextInput,
Button,
StyleSheet,
Image,
KeyboardAvoidingView,
Platform,
} from 'react-native'
import { useState } from 'react'
export default function App() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [errors, setErrors] = useState({})
const validateForm = () => {
let errors = {}
if (!username) errors.username = 'Username is require'
if (!password) errors.password = 'Password is require'
setErrors(errors)
return Object.keys(errors).length === 0
}
return (
<KeyboardAvoidingView
behavior='padding'
keyboardVerticalOffset={Platform.OS === 'ios' ? 100 : 0}
style={styles.container}>
<View style={styles.form}>
<Image
style={styles.image}
source={require('./assets/adaptive-icon.png')}
/>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
value={username}
onChangeText={setUsername}
/>
{errors.username ? <Text style={styles.errorText}>{errors.username}</Text> : null}
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
value={password}
onChangeText={setPassword}
/>
{errors.password ? <Text style={styles.errorText}>{errors.password}</Text> : null}
<Button
title='Login'
onPress={() => {}}
/>
</View>
</KeyboardAvoidingView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
image: {
width: 200,
height: 400,
alignSelf: 'center',
marginBottom: 50,
},
})
- This way, our users will know exactly what they missed or what they need to correct.
Styling the Error Text
-For the error text styles, we'll set color to red and a margin bottom.
import {
View,
Text,
TextInput,
Button,
StyleSheet,
Image,
KeyboardAvoidingView,
Platform,
} from 'react-native'
import { useState } from 'react'
export default function App() {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [errors, setErrors] = useState({})
const validateForm = () => {
let errors = {}
if (!username) errors.username = 'Username is require'
if (!password) errors.password = 'Password is require'
setErrors(errors)
return Object.keys(errors).length === 0
}
return (
<KeyboardAvoidingView
behavior='padding'
keyboardVerticalOffset={Platform.OS === 'ios' ? 100 : 0}
style={styles.container}>
<View style={styles.form}>
<Image
style={styles.image}
source={require('./assets/adaptive-icon.png')}
/>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder='Enter your username'
value={username}
onChangeText={setUsername}
/>
{errors.username ? <Text style={styles.errorText}>{errors.username}</Text> : null}
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder='Enter your password'
secureTextEntry
value={password}
onChangeText={setPassword}
/>
{errors.password ? <Text style={styles.errorText}>{errors.password}</Text> : null}
<Button
title='Login'
onPress={() => {}}
/>
</View>
</KeyboardAvoidingView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
backgroundColor: '#f5f5f5',
},
form: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
label: {
fontSize: 16,
marginBottom: 5,
fontWeight: 'bold',
},
input: {
height: 40,
borderColor: '#ddd',
borderWidth: 1,
marginBottom: 15,
padding: 10,
borderRadius: 5,
},
image: {
width: 200,
height: 400,
alignSelf: 'center',
marginBottom: 50,
},
errorText: {
color: 'red',
marginBottom: 10,
},
})
This will make sure our errors stand out.
Conclusion
And there you have it—simple yet effective form validation.
But we are not done yet. In our next video, we will be handling form submission.
We will learn when to call this
validateFormfunction and how to log form values if the form is indeed valid.
Section 9: Networking
=> Networking
Intro
In this section of our course, we will delve into networking in React Native.
Our primary focus will be on fetching and submitting data to an API.
Along the way, we'll tackle loading states, error handling, and utilize the FlatList component to display our data.
Section Goals
Our goal in this section is to work with React Native code, focusing on networking.
We'll use the JsonPlaceholder API, a free online REST API, to obtain fake data.
This API is useful for tutorials, testing, sharing code examples, and more.
We'll primarily use the
/postsendpoint.
React Query and Tan Stack Query
You can also use libraries like React Query or Tan Stack Query (formerly known as react-query).
However, in this section, we'll stick to plain React Native.
We might explore Tan Stack Query in a separate section later in the series.
Getting Started
To follow along, create a new Expo project called "react-native-networking" using the following command:
npx create Expo app RN networking
JsonPlaceholder API
We'll use the JsonPlaceholder API for our examples.
This free API offers various endpoints for posts, comments, albums, etc.
Our focus will be on the
/postsendpoint.The API returns 100 posts with post IDs ranging from 1 to 100.
Each post has a user ID, title, and body.
While the text may not make sense, it's suitable for our purposes.
We can limit the number of posts returned by using a query parameter, e.g.,
/posts?_limit=10.
Next Steps
In the next video, we'll make a GET request from our React Native application.
We'll retrieve post titles and bodies from the API and display them in the UI.
Conclusion
- Now that you're familiar with the JsonPlaceholder API and our goals for this section, let's proceed with our networking journey. See you in the next video!
=> GET Requests
Introduction
In our last video, we learned about the Jsonplaceholder API.
In this video, we will fetch data from this API within a React Native component and display it in our UI.
Clearing Existing Code
Start by clearing out the existing JSX and styles in
app.js.Import
SafeAreaViewand add it to our JSX, setting thestyleprop toStyles.container.For the
containerstyle, leaveflexas1to occupy the entire available space.Change the background color to
#F5F5F5.Remove
alignItemsandjustifyContent, and instead add top padding for Android equal toStatusBar.currentHeight. Don't forget to importStatusBarfromreact-native.
Nest a
Viewcomponent within theSafeAreaView, assign thestyleprop toStyles.listContainer.The
listContainerstyle will haveflexset to1and horizontal padding.Import
StyleSheetif not already imported.
Fetching Data
Define an async function named
fetchData.In
fetchData, make a fetch request to the Jsonplaceholder post endpoint:const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')Convert the response to JSON:
const data = await response.json()Store the JSON array of posts in a state variable named
postList. ImportuseStatefromreactand initialize it with an empty array.
Component Mounting
Import
useEffectfromreactand use it with a callback function and an empty dependency array.Inside the callback, invoke
fetchDatato fetch data when the component mounts.
Rendering Data
Use the
FlatListcomponent to render the data.- Import
FlatListfromreact-native.
- Import
Provide the
dataandrenderItemprops toFlatList:data={postList}
renderItem={({ item }) => (
// JSX for each post item
)}Destructure
itemand return aViewcomponent with nestedTextcomponents to display each post's title and body.Style each component using the
card,titleText, andbodyTextstyles.
Enhancing the UI
Add separators, a header, and a footer to the
FlatListas props:ItemSeparatorComponent,ListEmptyComponent,ListHeaderComponent, andListFooterComponent.
Style the header and filter text accordingly.
Conclusion
You have now learned how to make a GET request and bind the response to the UI.
In the next video, we will learn how to add a loading state while data is fetched in the background.
=> Loading State
Intro
In our last video, we successfully fetched data from the JSON Placeholder API and displayed it in our UI.
In this video, we will explore how to implement a loading state while our data is being fetched in the background.
Implementing Loading State
To add a loading state, we will need to introduce a new state variable. Let's name it
isLoading.With the
setupfunction, setisLoadingand the initial value totrue.
set isLoading true
Once our data has been fetched, we will update the state to
false.In our
fetchDatafunction, right after invokingsetPostListfunction, we will callset isLoadingand set it tofalse.
set isLoading false
Now, based on the value of
isLoading, we will conditionally render a loading spinner.Start by importing the
ActivityIndicatorcomponent from React Native.
import { ActivityIndicator } from 'react-native'
- Then, just before the return statement, add:
if isLoading
return (
<SafeAreaView style={Styles.loadingContainer}>
<ActivityIndicator size="large" color="blue" />
<Text>Loading</Text>
</SafeAreaView>
);
- Let's also define the styles for this
loadingContainer.
const Styles = StyleSheet.create({
loadingContainer: {
flex: 1,
backgroundColor: 'background-color',
paddingTop: StatusBar.currentHeight,
justifyContent: 'center',
alignItems: 'center',
},
})
- We want the loading indicator right in the center of our screen.
Visualizing the Loading UI
If we now save the file and head back to the UI, with iPhone in focus, press R to refresh.
You will briefly see the loading indicator.
Given that the API is quite fast, the loading state might not be visible for long.
To better visualize the loading UI during longer API response times, you can temporarily comment out
set isLoadingtofalse.
// set isLoading false
You can see the loading indicator and the loading text.
This is the UI a user would see while data is being fetched in the background.
Conclusion
As you can see, implementing a loading state for network requests in React Native is straightforward and enhances the user experience.
Thank you for watching, and I'll see you in the next one.
=> Pull to refresh
Intro
Welcome back, everyone. In this video, we're going to explore how to implement the pull-to-refresh functionality using the
FlatListcomponent in React Native.This feature is particularly useful when you want to refresh the data displayed in your app without having to restart it.
Given that we are already fetching posts from the JSON Placeholder API, let's see how we can refresh this data.
Implementing Pull-to-Refresh
To begin, let's introduce a new state variable to keep track of the refreshing state. We will call it
refreshingand set its initial value tofalse.Next, we will assign this
refreshingstate to therefreshingprop of ourFlatListcomponent. Sorefreshingis equal to the state variablerefreshing.We will also provide the
onRefreshprop on theFlatListcomponent, which expects a function to be executed when theFlatListis pulled down for refreshing. Let's assign it a function namedhandleRefresh.
Defining the handleRefresh Function
Now, let's define this function. So, right after
fetchData, consthandleRefresh.Within the function, we call
setRefreshingand set it totrue.We will then call the
fetchDatafunction but this time pass20as the limit to fetch an additional10items on refresh.We will then call
setRefreshingonce more and set it back tofalse.
Conclusion
And that is essentially all there is to it. We don't need to add any additional JSX as the two props,
refreshingandonRefresh, on theFlatListcomponent automatically render a default loading spinner when the list is pulled down.Now, if we save the file and return to our UI, you can see we have
1-10posts to begin with.If I scroll all the way to the top and pull down, we will briefly see a loading indicator.
But after pulling to refresh, we should now see a total of
20cards in our UI.Our code is working as expected.
The pull-to-refresh gesture is a common pattern in mobile development, and I wanted to highlight its implementation in this video.
I hope you found it straightforward.
Alright, in the next video, let's learn how to submit data to an API endpoint.
Thank you for watching, and please do consider subscribing to the channel and leaving a like if you're enjoying the content.
=> POST Request
Introduction
In this video, we will explore how to make a POST request from a React Native app.
We will use the jsonplaceholder API as an example, posting a new post title and body.
Using Jsonplaceholder API
The Jsonplaceholder API accepts POST requests to the
/postsendpoint, allowing us to focus on the React Native code.For more information, refer to the guide on creating a resource.
Step 1: Create State Variables
Begin by creating state variables to track the post title and body.
Introduce a variable to monitor the data submission process (
isPosting), initially set tofalse.
Step 2: Bind Variables to UI Elements
Wrap the current JSX with a React fragment.
Include a
Viewabove our list withstyleset toStyles.inputContainer.Inside the
View, add two text inputs for post title and body.Set
styletoStyles.input.Set
placeholderto "post title".Set
valueto the state variablepostTitle.Use
onChangeTextto callsetPostTitle.Duplicate this for post body.
Include a button to submit the data to the API.
Set
titleto conditionally render the button text based on theisPostingstate.Disable the button when the submission is in progress.
Define
onPressasaddPost(yet to be defined).Set
disabledtoisPosting.
Styling
Define the styles for the input container and text input.
inputContainerstyle: white background, padding, border radius, width, margin.inputstyle: height, border color, width, margin bottom, padding, border radius.
Make sure to import the
Buttoncomponent at the top.
Step 3: Define Button Click Handler
Uncomment
onPressand define theaddPostfunction.Inside
addPost, setisPostingtotrueat the start to disable the "Add Post" button.Make a POST request using
fetch.URL:
/postsMethod: POST
Headers: Content-Type - application/json
Body: JSON.stringify({ title: postTitle, body: postBody })
Convert the response to JSON and store it as
newPost.Update the post list by prepending
newPostto it.Reset the state of the form:
postTitleandpostBodyto empty strings, andisPostingtofalse.
Conclusion
This is how you make a POST request from a React Native component.
In this example, we used
fetch, but you can use other libraries like Axios or TenStack Query.
=> Error Handling
Intro
- Welcome back, in this video, we will add the final touch to our networking code by handling errors when fetching and submitting data. Let's get started.
Step 1: Define Error State Variable
Define a state variable to track any errors that might occur.
Use the
setErrorsetter function to seterror, and the initial value is an empty string.
Step 2: Try-Catch Blocks for Fetch and Post
Add try-catch blocks to our fetch data and add post functions to handle any errors that might occur during the fetch or post operations.
In the try block, reset any error that was present, and within the catch block, call
setErrorwith a message "failed to fetch post list" for error feedback to the user.
try {
// Fetch data or add post
} catch (error) {
console.log('Error fetching data or adding new post: ', error)
setError('failed to fetch post list') // Or "failed to add new post"
}
Step 3: Render UI Based on Error State
Use the error state to render appropriate UI to the user.
If there is an error present, render a view component with the text from the
errorstate variable. Otherwise, render the existing JSX.
return (
<SafeAreaView>
{error ? (
<View style={Styles.errorContainer}>
<Text style={Styles.errorText}>{error}</Text>
</View>
) : (
// Existing JSX
)}
</SafeAreaView>
);
Error Styles
- Define styles for the error container and text.
const Styles = StyleSheet.create({
errorContainer: {
backgroundColor: 'red',
padding: 10,
borderRadius: 5,
width: '80%',
margin: 10,
alignItems: 'center',
},
errorText: {
color: 'white',
fontSize: 16,
textAlign: 'center',
},
})
Note on Handling Localhost URLs in Android Emulator
Localhost URLs do not work from the Android emulator.
Consider using your computer's IP address instead of "localhost" for API requests from Android devices.
Debugging Features in React Native
Pressing
Command + Don iOS brings up the developer menu.Two debugging options:
- Show Element Inspector: Inspect elements, view the Box model, see styles applied.
- JavaScript Debugger: Inspect network requests, view API calls in the debugger.
Conclusion
Handling errors in networking in React Native.
Debugging features in React Native for iOS simulator.
Stay tuned for more advanced topics in the upcoming sections of the series.
Section 10: Navigation
=> Navigation
Intro
In this 10th section of our course, we're going to dive into navigation in React Native.
At the heart of any mobile application is navigation; it's the mechanism that allows users to move across different screens, access features, and generally use your app effectively.
Solutions for handling navigation
In React Native, a go-to solution for handling navigation is the React Navigation library.
It's worth mentioning that Expo has its own built-in routing feature, but it is exclusive to Expo projects.
On the other hand, React Navigation works both with and without Expo. In React Native apps, we will explore Expo's router in a later section of the series and focus on React Navigation in this section.
React Navigation provides a variety of Navigators like stack, drawer, and tab Navigators.
Stack Navigators provide a way for your app to transition between screens, where each new screen is placed on top of a stack.
Drawer Navigator renders a navigation drawer on the side of the screen, which can be opened and closed via gestures.
Tab Navigator at the bottom of your screen lets you easily switch between different routes. We will look at examples of all three.
Setting Up the Project
For this section, I have set up a new Expo project called "RN Navigation." If you wish to code along, create a similar project using the command:
npx create-expo-app RN-navigationIn this project folder, we're going to install React Navigation as a dependency. You can find the command in the React Navigation docs on the "Getting Started" page. Copy the command:
npm install @react-navigation/nativeand run it in your terminal.
We will also install two dependencies:
react-native-screensandreact-native-safe-area-context. Copy the command:npm install react-native-screens react-native-safe-area-contextand paste it in your project folder.
Finally, to work with React Navigation, we need a wrapping component for the whole app, and that is the
NavigationContainercomponent. You can go back to the doc, scroll down to the section on Navigation Container, copy the code, and paste it inApp.tsx. We don't really need to import React; the rest of the code we will write in this section will go in between the opening and closing tags ofNavigationContainer.Join me in the next video, where we will kick things off by setting up stack navigation in our React Native app. I'll see you in the next one.
=> Stack Navigation
Intro
Now that we have React Navigation in place, it's time to dive into one of its fundamental Navigators, which is the stack Navigator.
Stack navigation follows a straightforward principle: each new screen is stacked on top of the previous one. It's like a deck of cards - when you navigate to a new screen, a new card is placed on top of the stack, and when you navigate back, the top card is removed, revealing the previous screen.
This navigation pattern is common in many mobile apps, allowing users to drill down into detailed views and then retrace their steps when done.
It's particularly useful in scenarios where a linear flow of screens is required, for example, viewing a list of items, tapping on an item to see its details, and then tapping on a link contained within to view more details.
Stack Navigator and Native Stack Navigator
The React Navigation library offers two Navigators for stack navigation: Stack Navigator and Native Stack Navigator.
Stack Navigator: This is a JavaScript-based Navigator that offers a high degree of customization, making it a great choice for apps that require a unique navigation experience.- However, this customization comes at the cost of performance, especially when compared to its counterpart, the Native Stack Navigator.
Native Stack Navigator: The Native Stack Navigator leverages the native navigation constructs of iOS and Android, providing better performance and a more native feel to the transitions and gestures.- The caveat is that it might not offer the same level of customization as the Stack Navigator.
Setting Up the Project
First, we have to install the Native Stack Navigator library in our project. Go to the React Navigation docs, and under Navigators, select Native Stack. Copy the installation command:
npm install @react-navigation/native-stackand paste it in the terminal.
Next, in
App.js, at the top, import thecreateNativeStackNavigatorfunction fromreact-navigation/native-stackand invoke it to create a Native Stack Navigator instance.Now, set up the Navigator within our
Appcomponent. WithinNavigationContainer, usecreateNativeStackNavigator, and within this, usestack.Screen. This component accepts anameprop (which we have set to "home") and acomponentprop (which refers to the React Native component that will render a view).Let's define that component in the project. Create a new folder called
screens, and within it, create a new file,HomeScreen.js. Define a simple React Native component that renders the text "Home Screen."In
App.js, importHomeScreenand assign it to thecomponentprop onstack.Screen.We now have one screen defined in our application. Of course, navigation isn't of much use with just one screen, so let's create a second one. Create a new file,
AboutScreen.js, in thescreensfolder and define a similar component, but change all occurrences of "home" to "about."In
App.js, duplicate thestack.Screenline, changingnameto "aboutScreen," and make sure to import the component at the top.Our basic stack Navigator has been set up. If you now run the app using
npm start, check the two devices, and you will see the "Home Screen" rendered by default at the top. We also have a header of sorts with thenameprop onstack.Screendisplayed as the title.The library also takes care of rendering the content within a
SafeAreaView, avoiding the notch at the top. By default, the topmost screen within the Navigator is the initial screen. You can change this by setting theinitialRouteNameprop oncreateNativeStackNavigator.Let's set it to "aboutScreen," which is the name of the "About" screen.
Save the file, restart the server, and you can see the "About Screen" is displayed as the initial screen. But how do we navigate between the two screens? Let's learn that in the next video.
=> Navigation between Screens
Intro
Now that we have two screens in our stack Navigator (home and about), let's explore how we can navigate from one to the other.
We have two primary ways to handle navigation between screens: using the
navigationprop and theuseNavigationhook. Let's explore both methods and understand their use cases.
Using the navigation prop
Every screen component in your application is provided with the
navigationprop automatically by React Navigation.The
navigationprop has various methods to initiate navigation actions, one of which isnavigate.Let's see how we can use that method to navigate from our home screen to the about screen:
At the top, import the
Buttoncomponent fromreact-native.Below the
Textelement, invoke theButtoncomponent withtitleset to "Go to About" and specify theonPressprop to navigate to the about screen.In the
onPresshandler, destructure thenavigationprop on the home screen, and within an arrow function, callnavigation.navigatewith the name of the screen, which is "about."
If we save the file and change the initial route name to "home" (which is also the default route), press R to restart the app. We should see the "Go to About" button on both iOS and Android.
- Click on "Go to About," and we are navigated to the about screen. It's really simple.
Using the useNavigation hook
Alternatively, if you prefer using hooks, React Navigation provides a
useNavigationhook.Import it at the top and then invoke it within the component:
const navigation = useNavigation();
Delete the
navigationprop, and everything else remains the same. Restart the app, and we see the behavior remains the same.Now you might ask when to use one over the other.
The
navigationprop is easy to use and doesn't require any additional import if you're within a screen component.However, the
useNavigationhook can be used in any component, not just screen components, making it a more flexible option when you have nested components or are working with utility components that need to initiate navigation.So, my recommendation would be to use the
navigationprop for all screen components and use theuseNavigationhook only when it is necessary.Back in the UI, when you navigate from the home screen to the about screen, you can see that React Navigation keeps the home screen in the stack and adds the about screen on top of it.
If you press the back button, it pops the about screen off the stack and navigates back to the home screen, following the last-in-first-out principle of stack navigation.
This stacking behavior ensures a natural navigation flow within your app, allowing users to move forward to new screens or step back to previous screens effortlessly.
As an exercise, I would encourage you to try and add a button in the about screen to navigate to the home screen.
Let me know in the comment section if you were able to achieve that.
Now that you have grasped the basics of navigating between screens, join me in the next video where we will explore passing data between screens.
=> Passing Data between Screens
Intro
- In the previous video, we learned how to navigate between screens. In this video, we will learn how to pass data while navigating. Let's get straight into the code.
Passing Data Between Screens
For our example, let's pass a
nameparameter from thehomescreen to theaboutscreen and render thatnamevalue in theaboutscreen.The
navigation.navigatemethod, which we have already used to navigate between screens, also accepts a second argument: a route parameters object. This object can contain the data you want to pass to the new screen.Let's specify a key-value pair:
nameset to the string "WhichWas." We can access this parameter on theaboutscreen using theroute.paramsprop. This prop is provided to all screen components.Within the component, destructure
namefromroute.paramsand update the JSX to render{name}.If we now save the files, start our application, and go to the devices and then to the
aboutscreen, we see the text "about WhichWas." We are able to pass data between screens.
Setting Default Parameter Value
It is also possible to set a default parameter value. In
app.js, on theaboutscreen, specifyinitialParams, which is an object, and setnameto "guest."Now, if we remove the data when navigating from the
homescreen, restart the application, and go to the devices, and then to theaboutscreen, we see the text "about guest." "Guest" is the default value for thenameparameter.
Updating Parameters
It's also possible to update the parameters from the given screen using the
navigationprop. In theaboutscreen, destructurenavigationand in the JSX, add a newButton. Make sure to import the component fromreact-native.Specify
titleas "Update the name" and ononPress, invokenavigation.setParams, passing in an object withnameset to "CodeEvolution."If we restart our application, go to the device, go to the
aboutscreen, and click on "Update the name," we see "about CodeEvolution."
Sending Data Back
You can also send data back to the previous screen in the same way. Create a
Buttonwith thetitle"Go back with data," and ononPress, usenavigation.navigateto go to thehomescreen withresultas "Data from about."In the
homescreen, destructurerouteand add anotherTextcomponent that rendersroute.params.result.If you now click on "Go back with data," we see "Data from about."
As you can see, React Navigation's parameter handling provides a seamless way to pass and retrieve data between screens, making your navigation structure more dynamic and responsive to user interaction.
Customizing Appearance and Behavior
- In the next video, we will dive deeper into customizing the appearance and behavior of our Navigator and screens.
=> Stack Navigation Options
Intro
- Now that we are familiar with basic navigation, it's time to explore some of the options that the stack Navigator supports in this video.
- We will dive into specifying a screen title, styling headers, and setting content styles.
Screen Title
- Every screen in our stack can display a title in the header serving as a handy guide for users to understand their current location within the app.
- By default, the
nameprop on the screen component is showcased as the screen title (e.g., "home" and "about"). - This can be customized by specifying a
titleoption.- Use the
optionsprop to specify an object with the keytitle, like this:
options: {
title: 'Welcome Home'
} - Use the
- The home screen title now reads "Welcome Home."
Styling Headers
- It's often desirable to style the header to resonate with the application's theme.
- This includes setting the background color, text color, and font weight among other attributes.
- To style the header, you can use the
headerStyleoption:- Set
background color,header tint color, andheader title style font weightas follows:
options: {
headerStyle: {
backgroundColor: "purple",
},
headerTintColor: "white",
headerTitleStyle: {
fontWeight: "bold",
},
} - Set
Header Left and Header Right
- You can add custom components to the left and right of the header.
- To add a button on the right-hand side of the title, use the
headerRightoption:- This should be equal to a function that returns a component.
- Import
PressableandTextcomponents if needed. - Example code:
options: {
headerRight: () => (
<Pressable onPress={() => alert("Menu button pressed")}>
<Text style={{ color: "white", fontSize: 16 }}>Menu</Text>
</Pressable>
),
}
Header Left
- Similar to
headerRight, there is also theheaderLeftoption, which adds an element on the left side of the header.
Styling Content
- To style the content of your screens, you can use the
contentStyleoption. - For instance, to set a background color, use:
options: {
contentStyle: {
backgroundColor: '#HEX_VALUE'
}
}
- This sets the background color to a hex value.
Uniform Style Across All Screens
- For a more uniform style across all screens in the stack, you need to lift these options to the
Stack Navigatorusing thescreenOptionsprop. - For example, you can specify
screenOptionsand move all styling options to it. - This ensures the same styling applies to all screens in the stack.
=> Dynamic Stack Navigator Options
Intro
- In the previous video, we dived into some of the options provided by the stack Navigator.
- We explored setting a title for the home screen, a background color for the header, and even a background color for the content.
Dynamically Set Header Title
- There are instances where you might want to dynamically set certain options, with the title being a prime example.
- Let's say, instead of a generic title like "about," we want to personalize it with the name of the person passed in as a parameter.
Stack.Screen Component
- One way to dynamically set the header title is directly from the
Stack.Screencomponent by passing a function to theoptionsprop. - In the context of our About screen component, we would specify
optionsas a function which returns an object. - The function receives the
routeprop from the screen, which we can destructure to access the desired parameters and use them as part of the title. - For example, you can set the title like this:
```javascript
options: ({ route }) => ({
title: route.params.name,
})
``` - When navigating from the Home screen and passing a
name, the title will display the passed-in name parameter.
Leveraging React Hook
- Alternatively, we can leverage a React hook to set a dynamic title.
- Comment out the
optionsprop on the About screen and head over to the About screen component. - Bring in
useLayoutEffectfrom React and invoke it within the component. - Pass in a function with a dependency array.
- Within the function, invoke
navigation.setOptionsand pass in the options object. - Set the title to
name, which we have extracted fromroute.params. - This approach is dependent on the
navigationprop as well as thename.
Use Effect vs. Use Layout Effect
- If we replace
useLayoutEffectwith justuseEffect, the title update might show a noticeable delay, which is not the smoothest user experience. useLayoutEffectis the recommended option for immediate updates.
Choosing Between the Two
- Use
Stack.Screenoptions when the title or navigation options are driven by Route parameters or are static. - Opt for
useLayoutEffectwhen the navigation options depend on the screen component's internal logic, state, or props or when they need to be updated post-render.
=> Drawer Navigation
- Having learned about stack navigation, it's time to switch gears and explore another essential Navigator, which is the
drawer Navigator. - Similar to how
stack Navigatorstacks screens one over the other,drawer Navigatorintroduces a hidden menu sliding from either side of the screen. It is particularly beneficial in apps with multiple main sections that require a neat and organized navigation structure.
Installation
- Let's understand better with code. First, we have to install the
drawer Navigatorpackage in our project. I will follow the documentation for this installation to make it easier for you. - In the
react navigation docsunderNavigator, click ondrawer. Scroll down and copy the installation command. - Within the project, paste the command:
npm install @react-navigation/drawer.
Library Installation
- Next, you need to install and configure the libraries that are required by the
drawer Navigator. That would bereact native gesture Handlerandreact native reanimated. - Copy the command and paste it in the terminal.
Code Separation
- Now rename
app.jstoapp stack.jsto separate the code from the previous tutorials. - Within the project folder, create a new file
app.js.
Importing Libraries
- Import
react native gesture Handler. Make sure it's at the top and there is nothing else before it. - Import
navigation containerfromreact navigation. - Import
create drawer navigatorfromreact navigation drawer. Invoke it and create adrawer navigationinstance that we can work with.
Missing Step
- Now there is one step missing from the docs. If you browse the docs for the
reanimatedpackage, you will come across this step two where we need to add thebabelplugin. - So, copy the plugins array and open
Babelconfiguration (babel.config.js) in the project. Paste it after presets and remove the three dots.
Clearing Cache
- In step three, they also recommend we clear the cache before starting the app. Let's add this
-Coption inpackage.jsonto start the script. - So,
npm start -C.
Component Creation
- Our installation and setup step is now complete. Next, within
app.js, create areact componentand default export the same. - Add the
navigation containerwithin the app component and insidenavigation container, invokedrawer.Navigator.
Defining Screens
- Define two screens in the
screensfolder: create adashboard screen.jsand asettings screen.js. - This component will render just a title.
Adding Screens
- In
app.js, on eachdrawer screencomponent, we specify anameprop, which is the label on our drawer. dashboardandsettings. We will also add acomponentprop to which we assign the individual screen,dashboard screenandsettings screen.
Restart Server
- We can now restart our server:
npm start. - Press
Ito run the app on iOS simulator andAto run on Android emulator. - If the app is still not working, press
Rto restart the application on the devices.
Using the Drawer
- We can now see an icon to toggle the drawer; a swipe from the left edge also reveals our drawer.
- From the drawer menu, we can navigate to the
dashboardandsettingsscreen. - In each screen, the
nameprop is displayed as the screen title:settingsanddashboard, which correspond to thenameprop.
Programmatic Navigation
- It's also possible to toggle the drawer programmatically. In
dashboard screen, importbuttoncomponent, destructure thenavigationprop, and add a button with title set totoggle drawer. - On press, we call
navigation.toggleDrawer.
Navigating Programmatically
- Along similar lines, you can use the
jump tomethod on thenavigationprop to navigate programmatically without the drawer UI. - Duplicate, change the title to
settings, and call thejump tomethod passing insettings. - Click on
settings, and we are navigated to the settings screen, which is set as the active item in the drawer.
Conclusion
- The drawer navigation is pretty straightforward. In the next video, let's take a look at some of the options we can specify on the drawer Navigator.
- Thank you for watching, and I'll see you in the next one.
=> Drawer Navigation Options
- Now that we have set up a basic
drawer Navigator, let's quickly take a look at some options to customize its appearance. - We will specify the
optionsprop on thedashboard screen.
Title Option
- The first option is
title. Let's assign the textmy dashboard. - This gets rendered as the header title as well as the drawer label, which is
my dashboard. - If you want a different drawer label, specify the
drawerLabeloption and assign a value. Let's go withdashboard label. - You can see the header title is now different from the drawer label.
Custom Colors
- Next, we can specify
drawer active tint coloranddrawer active background color. This affects the active item in the drawer.
Entire Drawer Color
- Finally, for the entire drawer color, we can specify
drawer content styleand setbackground colorto#C6CBEF. Don't forget the hash symbol. - Save the file, take a look at the device, and we can see the new color.
Further Customization
- Now, there are a few more options that you can use to customize the behavior of the drawer, but I would say they are often not necessary.
- Please refer to the docs if you wish to further customize the drawer's appearance and behavior.
- These five options are very helpful.
Conclusion
- Thank you for watching, and in the next video, let's take a look at tab navigation in React Native.
=> Tab Navigation
- Now that we have learned about stack and drawer navigations, let's learn about tab navigation in this video.
- Tab navigation offers a way to switch between different screens by tapping on a tab, which is usually displayed at the bottom of the screen.
- It's a common and intuitive navigation pattern found in many apps, providing a seamless user-friendly experience.
Installation
- First, we have to install the
bottom tabs Navigatorlibrary in our project. I'll follow the documentation for this installation to make it easier for you. - Open the page on
bottom tabsunderNavigatorsand copy the installation command. - Run it in the terminal:
npm install @react-navigation/bottom-tabs.
Code Separation
- Now rename
app.jstoapp draw.jsto separate the code from the previous tutorials. - Within the project folder, create a new
app.jsfile.
Importing Libraries
- Import
navigation containerfromreact navigation. - In the next line, import
create bottom tab Navigatorfunction fromreact navigation bottom tabs. - Invoke the function and create a
tab Navigatorinstance.
Creating Components
- Next, create a
react componentand default export the same. - Add the
navigation containerwithin the app component and insidenavigation container, invoketab.Navigator. - As children to
tab.Navigator, invoketab.Screenonce for each screen you wish to include as a tab.
Defining Screens
- Let's reuse the
settings screenfrom before and create two additional screens. - In the
screensfolder, create aprofile screen.jsand acourse list screen.js. - Copy-paste the code from
settings screenand change the component name and the text that is rendered. - So,
profileandcourse list.
Configuring Tab Screens
- Back in
app.js, on eachtab.Screencomponent, we specify anameprop, which is the label on our tabs:course list,profile, andsettings. - We also assign a
componentprop to which we assign the individual screens. - Make sure to import the component at the top. For example,
profile screenandsettings screen.
Tab Navigation in Action
- If we head back to the devices, we can see the tab navigation at the bottom.
- We have three tabs: one for
course list, one forprofile, and one forsettings. - The same in Android. The tabs allow for easy navigation to the respective screens.
- The
nameprop ontab.Screenis displayed as the label, which you see at the bottom, as well as the title in the header.
Conclusion
- As you can see, it is really simple to create a tabbed navigation in React Native.
- In the next video, let's take a look at some options for the tab Navigator.
- Thank you for watching, and I'll see you in the next one.
=> Tab Navigation Options
- Now that we have set up a basic tab Navigator, let's look at some of the options we can specify.
Screen Options
Let's start with screen options on the
tab Navigator.First, we have
tab bar label position. By default, it is set tobelow icon, which is what you see currently - label below the icon.We can change this to
beside icon, and the label is now to the right of the icon. Typically,below iconis set for mobile devices, andbeside iconfor iPad and tablets. Let's stick withbelow iconas we are dealing with two mobile devices.The next option is
tab bar show label. This is set toTrueby default. If we set it tofalse, the tab label is hidden, and only the icon is displayed. We do want the label, so I will flip it back toTrue.Next, we have
tab bar active tint color, which sets the active tab font color. Let's set it topurple, and you can see the new purple color being reflected.There is also
tab bar inactive color(inactive tint color), which applies the color for inactive tabs. It is set tograyby default, but you can go ahead and change it based on your theme.
Tab Screen Options
By default, the
Tab screen nameis rendered as thetab bar label. You can change that using thetab bar labeloption on theprofile tab.screencomponent. Let's specifyoptions, which is an object, and we settab bar labeltomy profile. If we head back to the devices, we can see the updated tab bar label. The header title, though, remains the same as thenameprop.Next, you would want to customize the icon in the tab. The easiest way to add icons is using Expo icons. Import
ion iconsfrom@Expo/Vector icons. As an option, specify thetab bar iconoption. This is going to returnion iconswithnameequal toperson, which is one of the icons present, andsizeequal to20. Take a look at the devices, and we see thepersonicon being rendered for theprofiletab.To ensure the icon picks up on the active and inactive tint color, you can destructure a
colorprop and assign it to thecolorprop onION icons. So,coloris equal tocolor, and you can see the icon is now purple or gray instead of black.Finally, you can specify
tab bar badgeto add a badge to the icon. This is particularly useful if you have a notifications tab or an inbox tab that requires the user's attention to items on the screen.
Additional Options
- Apart from these basic options, you have a few more which may come in handy depending on your requirements. It is also possible to define a completely custom
tab bar componentand pass it intoreact navigation. Unless you have a lot of time to work on that, I would recommend you stick to the default bottom tab thatreact navigationoffers.
Conclusion
- Now that we have seen the three different types of Navigators in react native, in the next video, let's learn how to Nest Navigators.
- Thank you for watching. Please do consider subscribing to the channel, and I'll see you in the next one.
=> Nesting Navigators
- For the final video in this section on react native navigation, let's learn how to Nest Navigators.
- Nesting Navigators allows us to combine the powers of different types of Navigators, creating a seamless and organized user experience.
- It's like having a main road with smaller branching lanes, each having its own set of rules yet interconnected.
Nesting Example
- Let's dive into code and explore how to Nest Navigators in our react native app.
- For our example, we will Nest a stack Navigator within a tab Navigator. We're going to reuse the Navigators we have already created.
Modifying app.js
- Let's begin by making a small change in
app.js, which contains our stack Navigator. - Create and export a constant called
about stackwhich returns the stack Navigator. - So, from the function
app, cutstack Navigatorand return it fromabout stack. - Invoke
about stackwithinnavigation containerto not break any code we have written before.
Creating a New Tab Screen
- For this video, we will only be using
about stack. - Back in
app.js, where our tab Navigator is present, create a newtab screen. The name will readabout stack, andcomponentwill be equal to the newabout stackcomponent we've exported fromapp stack.js. - Make sure to import the component at the top.
Nested Navigation
- That is pretty much it. We have nested
about stack, which is a stack Navigator, within our tab Navigator. - If we head back to the devices, we should now see a fourth tab called
about stack. Clicking on the tab will present the home screen, which is part of theabout stack Navigator. We can navigate to theabout screenfrom here.
Handling Headers
- We do, however, have two headers, one from each of the Navigators. Ideally, you would want the stack Navigator to control the heading. In this scenario, to hide the tab Navigator heading on
tab screen, specifyoptions, which is an object. - Set
headerShowntofalse.
Conclusion
- If we head back to the devices, we now have only one header from the stack Navigator. This is how you Nest Navigators with react navigation.
- Ideally, you should try to achieve the behavior you want with as little nesting as possible. Not only the code but also the UX will be confusing with many levels of nesting.
- With that, we come to the end of this section on navigation in react native. We've learned about react navigation, which is a go-to package for navigation in react native. We've learned about the three different types of Navigators, namely stack, drawer, and tab.
- We've also seen how to navigate to different screens with each of them and customize the look and feel as well.
- Thank you for watching. Please do leave a like if you're enjoying the content, and I'll see you in the next one.